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 wxMenu
*wxMenuBar::Remove(size_t pos
)
453 wxMenu
*menu
= wxMenuBarBase::Remove(pos
);
455 return (wxMenu
*) NULL
;
458 GtkMenuShell *menu_shell = GTK_MENU_SHELL(m_factory->widget);
460 printf( "factory entries before %d\n", (int)g_slist_length(m_factory->items) );
461 printf( "menu shell entries before %d\n", (int)g_list_length( menu_shell->children ) );
464 // unparent calls unref() and that would delete the widget so we raise
465 // the ref count to 2 artificially before invoking unparent.
466 gtk_widget_ref( menu
->m_menu
);
467 gtk_widget_unparent( menu
->m_menu
);
469 gtk_widget_destroy( menu
->m_owner
);
472 printf( "factory entries after %d\n", (int)g_slist_length(m_factory->items) );
473 printf( "menu shell entries after %d\n", (int)g_list_length( menu_shell->children ) );
476 if (m_invokingWindow
)
478 // OPTIMISE ME: see comment in GtkAppend
480 wxFrame
*frame
= wxDynamicCast( m_invokingWindow
, wxFrame
);
483 frame
->UpdateMenuBarSize();
489 static int FindMenuItemRecursive( const wxMenu
*menu
, const wxString
&menuString
, const wxString
&itemString
)
491 if (wxMenuItem::GetLabelFromText(menu
->GetTitle()) == wxMenuItem::GetLabelFromText(menuString
))
493 int res
= menu
->FindItem( itemString
);
494 if (res
!= wxNOT_FOUND
)
498 wxMenuItemList::Node
*node
= menu
->GetMenuItems().GetFirst();
501 wxMenuItem
*item
= node
->GetData();
502 if (item
->IsSubMenu())
503 return FindMenuItemRecursive(item
->GetSubMenu(), menuString
, itemString
);
505 node
= node
->GetNext();
511 int wxMenuBar::FindMenuItem( const wxString
&menuString
, const wxString
&itemString
) const
513 wxMenuList::Node
*node
= m_menus
.GetFirst();
516 wxMenu
*menu
= node
->GetData();
517 int res
= FindMenuItemRecursive( menu
, menuString
, itemString
);
520 node
= node
->GetNext();
526 // Find a wxMenuItem using its id. Recurses down into sub-menus
527 static wxMenuItem
* FindMenuItemByIdRecursive(const wxMenu
* menu
, int id
)
529 wxMenuItem
* result
= menu
->FindChildItem(id
);
531 wxMenuItemList::Node
*node
= menu
->GetMenuItems().GetFirst();
532 while ( node
&& result
== NULL
)
534 wxMenuItem
*item
= node
->GetData();
535 if (item
->IsSubMenu())
537 result
= FindMenuItemByIdRecursive( item
->GetSubMenu(), id
);
539 node
= node
->GetNext();
545 wxMenuItem
* wxMenuBar::FindItem( int id
, wxMenu
**menuForItem
) const
547 wxMenuItem
* result
= 0;
548 wxMenuList::Node
*node
= m_menus
.GetFirst();
549 while (node
&& result
== 0)
551 wxMenu
*menu
= node
->GetData();
552 result
= FindMenuItemByIdRecursive( menu
, id
);
553 node
= node
->GetNext();
558 *menuForItem
= result
? result
->GetMenu() : (wxMenu
*)NULL
;
564 void wxMenuBar::EnableTop( size_t pos
, bool flag
)
566 wxMenuList::Node
*node
= m_menus
.Item( pos
);
568 wxCHECK_RET( node
, wxT("menu not found") );
570 wxMenu
* menu
= node
->GetData();
573 gtk_widget_set_sensitive( menu
->m_owner
, flag
);
576 wxString
wxMenuBar::GetLabelTop( size_t pos
) const
578 wxMenuList::Node
*node
= m_menus
.Item( pos
);
580 wxCHECK_MSG( node
, wxT("invalid"), wxT("menu not found") );
582 wxMenu
* menu
= node
->GetData();
585 wxString
text( menu
->GetTitle() );
586 for ( const wxChar
*pc
= text
.c_str(); *pc
; pc
++ )
588 if ( *pc
== wxT('_') )
590 // '_' is the escape character for GTK+
600 void wxMenuBar::SetLabelTop( size_t pos
, const wxString
& label
)
602 wxMenuList::Node
*node
= m_menus
.Item( pos
);
604 wxCHECK_RET( node
, wxT("menu not found") );
606 wxMenu
* menu
= node
->GetData();
608 wxString
str( wxReplaceUnderscore( label
) );
610 menu
->SetTitle( str
);
614 GtkLabel
*label
= GTK_LABEL( GTK_BIN(menu
->m_owner
)->child
);
617 gtk_label_set( label
, wxGTK_CONV( str
) );
619 /* reparse key accel */
620 (void)gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV( str
) );
621 gtk_accel_label_refetch( GTK_ACCEL_LABEL(label
) );
626 //-----------------------------------------------------------------------------
628 //-----------------------------------------------------------------------------
630 static void gtk_menu_clicked_callback( GtkWidget
*widget
, wxMenu
*menu
)
633 wxapp_install_idle_handler();
635 int id
= menu
->FindMenuIdByMenuItem(widget
);
637 /* should find it for normal (not popup) menu */
638 wxASSERT_MSG( (id
!= -1) || (menu
->GetInvokingWindow() != NULL
),
639 _T("menu item not found in gtk_menu_clicked_callback") );
641 if (!menu
->IsEnabled(id
))
644 wxMenuItem
* item
= menu
->FindChildItem( id
);
645 wxCHECK_RET( item
, wxT("error in menu item callback") );
647 if (item
->IsCheckable())
649 bool isReallyChecked
= item
->IsChecked(),
650 isInternallyChecked
= item
->wxMenuItemBase::IsChecked();
652 // ensure that the internal state is always consistent with what is
653 // shown on the screen
654 item
->wxMenuItemBase::Check(isReallyChecked
);
656 // we must not report the events for the radio button going up nor the
657 // events resulting from the calls to wxMenuItem::Check()
658 if ( (item
->GetKind() == wxITEM_RADIO
&& !isReallyChecked
) ||
659 (isInternallyChecked
== isReallyChecked
) )
664 // the user pressed on the menu item: report the event below
667 menu
->SendEvent(id
, item
->IsCheckable() ? item
->IsChecked() : -1);
670 //-----------------------------------------------------------------------------
672 //-----------------------------------------------------------------------------
674 static void gtk_menu_hilight_callback( GtkWidget
*widget
, wxMenu
*menu
)
676 if (g_isIdle
) wxapp_install_idle_handler();
678 int id
= menu
->FindMenuIdByMenuItem(widget
);
680 wxASSERT( id
!= -1 ); // should find it!
682 if (!menu
->IsEnabled(id
))
685 wxMenuEvent
event( wxEVT_MENU_HIGHLIGHT
, id
);
686 event
.SetEventObject( menu
);
688 if (menu
->GetEventHandler()->ProcessEvent(event
))
691 wxWindow
*win
= menu
->GetInvokingWindow();
692 if (win
) win
->GetEventHandler()->ProcessEvent( event
);
695 //-----------------------------------------------------------------------------
697 //-----------------------------------------------------------------------------
699 static void gtk_menu_nolight_callback( GtkWidget
*widget
, wxMenu
*menu
)
701 if (g_isIdle
) wxapp_install_idle_handler();
703 int id
= menu
->FindMenuIdByMenuItem(widget
);
705 wxASSERT( id
!= -1 ); // should find it!
707 if (!menu
->IsEnabled(id
))
710 wxMenuEvent
event( wxEVT_MENU_HIGHLIGHT
, -1 );
711 event
.SetEventObject( menu
);
713 if (menu
->GetEventHandler()->ProcessEvent(event
))
716 wxWindow
*win
= menu
->GetInvokingWindow();
718 win
->GetEventHandler()->ProcessEvent( event
);
721 //-----------------------------------------------------------------------------
723 //-----------------------------------------------------------------------------
725 IMPLEMENT_DYNAMIC_CLASS(wxMenuItem
, wxObject
)
727 wxMenuItem
*wxMenuItemBase::New(wxMenu
*parentMenu
,
729 const wxString
& name
,
730 const wxString
& help
,
734 return new wxMenuItem(parentMenu
, id
, name
, help
, kind
, subMenu
);
737 wxMenuItem::wxMenuItem(wxMenu
*parentMenu
,
739 const wxString
& text
,
740 const wxString
& help
,
743 : wxMenuItemBase(parentMenu
, id
, text
, help
, kind
, subMenu
)
748 wxMenuItem::wxMenuItem(wxMenu
*parentMenu
,
750 const wxString
& text
,
751 const wxString
& help
,
754 : wxMenuItemBase(parentMenu
, id
, text
, help
,
755 isCheckable
? wxITEM_CHECK
: wxITEM_NORMAL
, subMenu
)
760 void wxMenuItem::Init(const wxString
& text
)
762 m_labelWidget
= (GtkWidget
*) NULL
;
763 m_menuItem
= (GtkWidget
*) NULL
;
768 wxMenuItem::~wxMenuItem()
770 // don't delete menu items, the menus take care of that
773 // return the menu item text without any menu accels
775 wxString
wxMenuItemBase::GetLabelFromText(const wxString
& text
)
779 for ( const wxChar
*pc
= text
.c_str(); *pc
; pc
++ )
781 if ( *pc
== wxT('_') )
783 // GTK 1.2 escapes "xxx_xxx" to "xxx__xxx"
789 #if GTK_CHECK_VERSION(2, 0, 0)
790 if ( *pc
== wxT('\\') )
792 // GTK 2.0 escapes "xxx/xxx" to "xxx\/xxx"
805 void wxMenuItem::SetText( const wxString
& str
)
807 // Some optimization to avoid flicker
808 wxString oldLabel
= m_text
;
809 oldLabel
= wxStripMenuCodes(oldLabel
.BeforeFirst('\t'));
810 oldLabel
.Replace(wxT("_"), wxT(""));
811 wxString label1
= wxStripMenuCodes(str
.BeforeFirst('\t'));
812 if (oldLabel
== label1
)
821 label
= (GtkLabel
*) m_labelWidget
;
823 label
= GTK_LABEL( GTK_BIN(m_menuItem
)->child
);
825 #if GTK_CHECK_VERSION(2, 0, 0)
826 // We have to imitate item_factory_unescape_label here
828 for (size_t n
= 0; n
< m_text
.Len(); n
++)
830 if (m_text
[n
] != wxT('\\'))
834 gtk_label_set_text_with_mnemonic( GTK_LABEL(label
), wxGTK_CONV(tmp
) );
837 gtk_label_set( label
, wxGTK_CONV( m_text
) );
840 (void)gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV(m_text
) );
841 gtk_accel_label_refetch( GTK_ACCEL_LABEL(label
) );
846 // it's valid for this function to be called even if m_menuItem == NULL
847 void wxMenuItem::DoSetText( const wxString
& str
)
849 // '\t' is the deliminator indicating a hot key
851 const wxChar
*pc
= str
;
852 while ( (*pc
!= wxT('\0')) && (*pc
!= wxT('\t')) )
854 if ((*pc
== wxT('&')) && (*(pc
+1) == wxT('&')))
856 // "&" is doubled to indicate "&" instead of accelerator
860 else if (*pc
== wxT('&'))
864 #if GTK_CHECK_VERSION(2, 0, 0)
865 else if ( *pc
== wxT('_') ) // escape underscores
867 // m_text << wxT("__"); doesn't work
869 else if (*pc
== wxT('/')) // we have to escape slashes
871 m_text
<< wxT("\\/");
873 else if (*pc
== wxT('\\')) // we have to double backslashes
875 m_text
<< wxT("\\\\");
878 else if ( *pc
== wxT('_') ) // escape underscores
882 else if (*pc
== wxT('/')) /* we have to filter out slashes ... */
884 m_text
<< wxT('\\'); /* ... and replace them with back slashes */
904 wxAcceleratorEntry
*wxMenuItem::GetAccel() const
909 return (wxAcceleratorEntry
*)NULL
;
912 // as wxGetAccelFromString() looks for TAB, insert a dummy one here
914 label
<< wxT('\t') << GetHotKey();
916 return wxGetAccelFromString(label
);
919 #endif // wxUSE_ACCEL
921 void wxMenuItem::Check( bool check
)
923 wxCHECK_RET( m_menuItem
, wxT("invalid menu item") );
925 if (check
== m_isChecked
)
928 wxMenuItemBase::Check( check
);
934 gtk_check_menu_item_set_state( (GtkCheckMenuItem
*)m_menuItem
, (gint
)check
);
938 wxFAIL_MSG( _T("can't check this item") );
942 void wxMenuItem::Enable( bool enable
)
944 wxCHECK_RET( m_menuItem
, wxT("invalid menu item") );
946 gtk_widget_set_sensitive( m_menuItem
, enable
);
947 wxMenuItemBase::Enable( enable
);
950 bool wxMenuItem::IsChecked() const
952 wxCHECK_MSG( m_menuItem
, FALSE
, wxT("invalid menu item") );
954 wxCHECK_MSG( IsCheckable(), FALSE
,
955 wxT("can't get state of uncheckable item!") );
957 return ((GtkCheckMenuItem
*)m_menuItem
)->active
!= 0;
960 wxString
wxMenuItem::GetFactoryPath() const
962 /* in order to get the pointer to the item we need the item text
963 _without_ underscores */
964 wxString
path( wxT("<main>/") );
966 for ( const wxChar
*pc
= m_text
.c_str(); *pc
; pc
++ )
968 if ( *pc
== wxT('_') )
970 // remove '_' unconditionally
980 //-----------------------------------------------------------------------------
982 //-----------------------------------------------------------------------------
984 IMPLEMENT_DYNAMIC_CLASS(wxMenu
,wxEvtHandler
)
988 m_accel
= gtk_accel_group_new();
989 m_factory
= gtk_item_factory_new( GTK_TYPE_MENU
, "<main>", m_accel
);
990 m_menu
= gtk_item_factory_get_widget( m_factory
, "<main>" );
992 m_owner
= (GtkWidget
*) NULL
;
994 /* Tearoffs are entries, just like separators. So if we want this
995 menu to be a tear-off one, we just append a tearoff entry
997 if(m_style
& wxMENU_TEAROFF
)
999 GtkItemFactoryEntry entry
;
1000 entry
.path
= (char *)"/tearoff";
1001 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
1002 entry
.callback_action
= 0;
1003 entry
.item_type
= (char *)"<Tearoff>";
1004 entry
.accelerator
= (gchar
*) NULL
;
1005 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); /* what is 2 ? */
1006 //GtkWidget *menuItem = gtk_item_factory_get_widget( m_factory, "<main>/tearoff" );
1009 // append the title as the very first entry if we have it
1012 Append(-2, m_title
);
1021 gtk_widget_destroy( m_menu
);
1023 gtk_object_unref( GTK_OBJECT(m_factory
) );
1026 bool wxMenu::GtkAppend(wxMenuItem
*mitem
)
1028 GtkWidget
*menuItem
;
1030 #if defined(USE_MENU_BITMAPS) || !GTK_CHECK_VERSION(1, 2, 0)
1031 bool appended
= FALSE
;
1034 // does this item terminate the current radio group?
1035 bool endOfRadioGroup
= TRUE
;
1037 if ( mitem
->IsSeparator() )
1039 GtkItemFactoryEntry entry
;
1040 entry
.path
= (char *)"/sep";
1041 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
1042 entry
.callback_action
= 0;
1043 entry
.item_type
= (char *)"<Separator>";
1044 entry
.accelerator
= (gchar
*) NULL
;
1046 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); /* what is 2 ? */
1048 /* this will be wrong for more than one separator. do we care? */
1049 menuItem
= gtk_item_factory_get_widget( m_factory
, "<main>/sep" );
1051 // we might have a separator inside a radio group
1052 endOfRadioGroup
= FALSE
;
1054 else if ( mitem
->IsSubMenu() )
1056 /* text has "_" instead of "&" after mitem->SetText() */
1057 wxString
text( mitem
->GetText() );
1059 /* local buffer in multibyte form */
1062 strcat( buf
, wxGTK_CONV( text
) );
1064 GtkItemFactoryEntry entry
;
1066 entry
.callback
= (GtkItemFactoryCallback
) 0;
1067 entry
.callback_action
= 0;
1068 entry
.item_type
= (char *)"<Branch>";
1069 entry
.accelerator
= (gchar
*) NULL
;
1071 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); /* what is 2 ? */
1073 wxString
path( mitem
->GetFactoryPath() );
1074 menuItem
= gtk_item_factory_get_item( m_factory
, wxGTK_CONV( path
) );
1076 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menuItem
), mitem
->GetSubMenu()->m_menu
);
1078 // if adding a submenu to a menu already existing in the menu bar, we
1079 // must set invoking window to allow processing events from this
1081 if ( m_invokingWindow
)
1082 wxMenubarSetInvokingWindow(mitem
->GetSubMenu(), m_invokingWindow
);
1084 #ifdef USE_MENU_BITMAPS
1085 else if (mitem
->GetBitmap().Ok()) // An item with bitmap
1087 wxString
text( mitem
->GetText() );
1088 const wxBitmap
*bitmap
= &mitem
->GetBitmap();
1090 menuItem
= gtk_pixmap_menu_item_new ();
1091 GtkWidget
*label
= gtk_accel_label_new ( wxGTK_CONV( text
) );
1092 gtk_misc_set_alignment (GTK_MISC (label
), 0.0, 0.5);
1093 gtk_container_add (GTK_CONTAINER (menuItem
), label
);
1094 guint accel_key
= gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV( text
) );
1095 gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (label
), menuItem
);
1096 if (accel_key
!= GDK_VoidSymbol
)
1098 gtk_widget_add_accelerator (menuItem
,
1100 gtk_menu_ensure_uline_accel_group (GTK_MENU (m_menu
)),
1104 gtk_widget_show (label
);
1106 mitem
->SetLabelWidget(label
);
1108 GtkWidget
* pixmap
= gtk_pixmap_new( bitmap
->GetPixmap(), bitmap
->GetMask() ? bitmap
->GetMask()->GetBitmap() : (GdkBitmap
* )NULL
);
1109 gtk_widget_show(pixmap
);
1110 gtk_pixmap_menu_item_set_pixmap(GTK_PIXMAP_MENU_ITEM( menuItem
), pixmap
);
1112 gtk_signal_connect( GTK_OBJECT(menuItem
), "activate",
1113 GTK_SIGNAL_FUNC(gtk_menu_clicked_callback
),
1116 gtk_menu_append( GTK_MENU(m_menu
), menuItem
);
1117 gtk_widget_show( menuItem
);
1119 appended
= TRUE
; // We've done this, don't do it again
1121 #endif // USE_MENU_BITMAPS
1122 else // a normal item
1124 // text has "_" instead of "&" after mitem->SetText() so don't use it
1125 wxString
text( mitem
->GetText() );
1127 // buffers containing the menu item path and type in multibyte form
1131 strcpy( bufPath
, "/" );
1132 strncat( bufPath
, wxGTK_CONV(text
), WXSIZEOF(bufPath
) - 2 );
1133 bufPath
[WXSIZEOF(bufPath
) - 1] = '\0';
1135 GtkItemFactoryEntry entry
;
1136 entry
.path
= bufPath
;
1137 entry
.callback
= (GtkItemFactoryCallback
) gtk_menu_clicked_callback
;
1138 entry
.callback_action
= 0;
1141 const char *item_type
;
1142 switch ( mitem
->GetKind() )
1145 item_type
= "<CheckItem>";
1149 if ( m_pathLastRadio
.empty() )
1151 // start of a new radio group
1152 item_type
= "<RadioItem>";
1153 wxString
tmp( wxGTK_CONV_BACK( bufPath
) );
1155 m_pathLastRadio
= tmp
;
1157 else // continue the radio group
1159 pathRadio
= m_pathLastRadio
;
1160 pathRadio
.Replace(wxT("_"), wxT(""));
1161 pathRadio
.Prepend(wxT("<main>/"));
1163 strncpy(bufType
, wxGTK_CONV(pathRadio
), WXSIZEOF(bufType
));
1164 bufType
[WXSIZEOF(bufType
) - 1] = '\0';
1165 item_type
= bufType
;
1168 // continue the existing radio group, if any
1169 endOfRadioGroup
= FALSE
;
1173 wxFAIL_MSG( _T("unexpected menu item kind") );
1177 item_type
= "<Item>";
1181 entry
.item_type
= (char *)item_type
; // cast needed for GTK+
1182 entry
.accelerator
= (gchar
*) NULL
;
1185 // due to an apparent bug in GTK+, we have to use a static buffer here -
1186 // otherwise GTK+ 1.2.2 manages to override the memory we pass to it
1188 char s_accel
[50]; // should be big enough, we check for overruns
1189 wxString
tmp( GetHotKey(*mitem
) );
1190 strncpy(s_accel
, wxGTK_CONV( tmp
), WXSIZEOF(s_accel
));
1191 s_accel
[WXSIZEOF(s_accel
) - 1] = '\0';
1192 entry
.accelerator
= s_accel
;
1193 #else // !wxUSE_ACCEL
1194 entry
.accelerator
= (char*) NULL
;
1195 #endif // wxUSE_ACCEL/!wxUSE_ACCEL
1197 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); /* what is 2 ? */
1199 wxString
path( mitem
->GetFactoryPath() );
1200 menuItem
= gtk_item_factory_get_widget( m_factory
, wxGTK_CONV( path
) );
1203 wxLogError( wxT("Wrong menu path: %s\n"), path
.c_str() );
1206 if ( !mitem
->IsSeparator() )
1208 wxASSERT_MSG( menuItem
, wxT("invalid menuitem") );
1210 gtk_signal_connect( GTK_OBJECT(menuItem
), "select",
1211 GTK_SIGNAL_FUNC(gtk_menu_hilight_callback
),
1214 gtk_signal_connect( GTK_OBJECT(menuItem
), "deselect",
1215 GTK_SIGNAL_FUNC(gtk_menu_nolight_callback
),
1219 mitem
->SetMenuItem(menuItem
);
1221 if ( endOfRadioGroup
)
1223 m_pathLastRadio
.clear();
1229 bool wxMenu::DoAppend(wxMenuItem
*mitem
)
1231 return GtkAppend(mitem
) && wxMenuBase::DoAppend(mitem
);
1234 bool wxMenu::DoInsert(size_t pos
, wxMenuItem
*item
)
1236 if ( !wxMenuBase::DoInsert(pos
, item
) )
1240 // GTK+ doesn't have a function to insert a menu using GtkItemFactory (as
1241 // of version 1.2.6), so we first append the item and then change its
1243 if ( !GtkAppend(item
) )
1246 if ( m_style
& wxMENU_TEAROFF
)
1248 // change the position as the first item is the tear-off marker
1252 GtkMenuShell
*menu_shell
= GTK_MENU_SHELL(m_factory
->widget
);
1253 gpointer data
= g_list_last(menu_shell
->children
)->data
;
1254 menu_shell
->children
= g_list_remove(menu_shell
->children
, data
);
1255 menu_shell
->children
= g_list_insert(menu_shell
->children
, data
, pos
);
1259 // this should be easy to do...
1260 wxFAIL_MSG( wxT("not implemented") );
1263 #endif // GTK 1.2/1.0
1266 wxMenuItem
*wxMenu::DoRemove(wxMenuItem
*item
)
1268 if ( !wxMenuBase::DoRemove(item
) )
1269 return (wxMenuItem
*)NULL
;
1271 // TODO: this code doesn't delete the item factory item and this seems
1272 // impossible as of GTK 1.2.6.
1273 gtk_widget_destroy( item
->GetMenuItem() );
1278 int wxMenu::FindMenuIdByMenuItem( GtkWidget
*menuItem
) const
1280 wxNode
*node
= m_items
.First();
1283 wxMenuItem
*item
= (wxMenuItem
*)node
->Data();
1284 if (item
->GetMenuItem() == menuItem
)
1285 return item
->GetId();
1286 node
= node
->Next();
1292 // ----------------------------------------------------------------------------
1294 // ----------------------------------------------------------------------------
1296 #if GTK_CHECK_VERSION(1, 2, 0) && wxUSE_ACCEL
1298 static wxString
GetHotKey( const wxMenuItem
& item
)
1302 wxAcceleratorEntry
*accel
= item
.GetAccel();
1305 int flags
= accel
->GetFlags();
1306 if ( flags
& wxACCEL_ALT
)
1307 hotkey
+= wxT("<alt>");
1308 if ( flags
& wxACCEL_CTRL
)
1309 hotkey
+= wxT("<control>");
1310 if ( flags
& wxACCEL_SHIFT
)
1311 hotkey
+= wxT("<shift>");
1313 int code
= accel
->GetKeyCode();
1328 hotkey
<< wxT('F') << code
- WXK_F1
+ 1;
1331 // TODO: we should use gdk_keyval_name() (a.k.a.
1332 // XKeysymToString) here as well as hardcoding the keysym
1333 // names this might be not portable
1334 case WXK_NUMPAD_INSERT
:
1335 hotkey
<< wxT("KP_Insert" );
1337 case WXK_NUMPAD_DELETE
:
1338 hotkey
<< wxT("KP_Delete" );
1341 hotkey
<< wxT("Insert" );
1344 hotkey
<< wxT("Delete" );
1347 hotkey
<< wxT("Up" );
1350 hotkey
<< wxT("Down" );
1353 hotkey
<< wxT("Prior" );
1356 hotkey
<< wxT("Next" );
1359 hotkey
<< wxT("Left" );
1362 hotkey
<< wxT("Right" );
1365 hotkey
<< wxT("Home" );
1368 hotkey
<< wxT("End" );
1371 hotkey
<< wxT("Return" );
1374 // if there are any other keys wxGetAccelFromString() may
1375 // return, we should process them here
1380 wxString name
= wxGTK_CONV_BACK( gdk_keyval_name((guint
)code
) );
1388 wxFAIL_MSG( wxT("unknown keyboard accel") );
1397 #endif // wxUSE_ACCEL
1400 //-----------------------------------------------------------------------------
1401 // substitute for missing GtkPixmapMenuItem
1402 //-----------------------------------------------------------------------------
1404 #ifdef USE_MENU_BITMAPS
1407 * Copyright (C) 1998, 1999, 2000 Free Software Foundation
1408 * All rights reserved.
1410 * This file is part of the Gnome Library.
1412 * The Gnome Library is free software; you can redistribute it and/or
1413 * modify it under the terms of the GNU Library General Public License as
1414 * published by the Free Software Foundation; either version 2 of the
1415 * License, or (at your option) any later version.
1417 * The Gnome Library is distributed in the hope that it will be useful,
1418 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1419 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1420 * Library General Public License for more details.
1422 * You should have received a copy of the GNU Library General Public
1423 * License along with the Gnome Library; see the file COPYING.LIB. If not,
1424 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
1425 * Boston, MA 02111-1307, USA.
1431 /* Author: Dietmar Maurer <dm@vlsivie.tuwien.ac.at> */
1433 #include <gtk/gtkaccellabel.h>
1434 #include <gtk/gtksignal.h>
1435 #include <gtk/gtkmenuitem.h>
1436 #include <gtk/gtkmenu.h>
1437 #include <gtk/gtkcontainer.h>
1442 static void gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass
*klass
);
1443 static void gtk_pixmap_menu_item_init (GtkPixmapMenuItem
*menu_item
);
1444 static void gtk_pixmap_menu_item_draw (GtkWidget
*widget
,
1445 GdkRectangle
*area
);
1446 static gint
gtk_pixmap_menu_item_expose (GtkWidget
*widget
,
1447 GdkEventExpose
*event
);
1449 /* we must override the following functions */
1451 static void gtk_pixmap_menu_item_map (GtkWidget
*widget
);
1452 static void gtk_pixmap_menu_item_size_allocate (GtkWidget
*widget
,
1453 GtkAllocation
*allocation
);
1454 static void gtk_pixmap_menu_item_forall (GtkContainer
*container
,
1455 gboolean include_internals
,
1456 GtkCallback callback
,
1457 gpointer callback_data
);
1458 static void gtk_pixmap_menu_item_size_request (GtkWidget
*widget
,
1459 GtkRequisition
*requisition
);
1460 static void gtk_pixmap_menu_item_remove (GtkContainer
*container
,
1463 static void changed_have_pixmap_status (GtkPixmapMenuItem
*menu_item
);
1465 static GtkMenuItemClass
*parent_class
= NULL
;
1469 #define BORDER_SPACING 3
1470 #define PMAP_WIDTH 20
1473 gtk_pixmap_menu_item_get_type (void)
1475 static GtkType pixmap_menu_item_type
= 0;
1477 if (!pixmap_menu_item_type
)
1479 GtkTypeInfo pixmap_menu_item_info
=
1481 (char *)"GtkPixmapMenuItem",
1482 sizeof (GtkPixmapMenuItem
),
1483 sizeof (GtkPixmapMenuItemClass
),
1484 (GtkClassInitFunc
) gtk_pixmap_menu_item_class_init
,
1485 (GtkObjectInitFunc
) gtk_pixmap_menu_item_init
,
1486 /* reserved_1 */ NULL
,
1487 /* reserved_2 */ NULL
,
1488 (GtkClassInitFunc
) NULL
,
1491 pixmap_menu_item_type
= gtk_type_unique (gtk_menu_item_get_type (),
1492 &pixmap_menu_item_info
);
1495 return pixmap_menu_item_type
;
1499 * gtk_pixmap_menu_item_new
1501 * Creates a new pixmap menu item. Use gtk_pixmap_menu_item_set_pixmap()
1502 * to set the pixmap wich is displayed at the left side.
1505 * &GtkWidget pointer to new menu item
1509 gtk_pixmap_menu_item_new (void)
1511 return GTK_WIDGET (gtk_type_new (gtk_pixmap_menu_item_get_type ()));
1515 gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass
*klass
)
1517 GtkObjectClass
*object_class
;
1518 GtkWidgetClass
*widget_class
;
1519 GtkMenuItemClass
*menu_item_class
;
1520 GtkContainerClass
*container_class
;
1522 object_class
= (GtkObjectClass
*) klass
;
1523 widget_class
= (GtkWidgetClass
*) klass
;
1524 menu_item_class
= (GtkMenuItemClass
*) klass
;
1525 container_class
= (GtkContainerClass
*) klass
;
1527 parent_class
= (GtkMenuItemClass
*) gtk_type_class (gtk_menu_item_get_type ());
1529 widget_class
->draw
= gtk_pixmap_menu_item_draw
;
1530 widget_class
->expose_event
= gtk_pixmap_menu_item_expose
;
1531 widget_class
->map
= gtk_pixmap_menu_item_map
;
1532 widget_class
->size_allocate
= gtk_pixmap_menu_item_size_allocate
;
1533 widget_class
->size_request
= gtk_pixmap_menu_item_size_request
;
1535 container_class
->forall
= gtk_pixmap_menu_item_forall
;
1536 container_class
->remove
= gtk_pixmap_menu_item_remove
;
1538 klass
->orig_toggle_size
= menu_item_class
->toggle_size
;
1539 klass
->have_pixmap_count
= 0;
1543 gtk_pixmap_menu_item_init (GtkPixmapMenuItem
*menu_item
)
1547 mi
= GTK_MENU_ITEM (menu_item
);
1549 menu_item
->pixmap
= NULL
;
1553 gtk_pixmap_menu_item_draw (GtkWidget
*widget
,
1556 g_return_if_fail (widget
!= NULL
);
1557 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
));
1558 g_return_if_fail (area
!= NULL
);
1560 if (GTK_WIDGET_CLASS (parent_class
)->draw
)
1561 (* GTK_WIDGET_CLASS (parent_class
)->draw
) (widget
, area
);
1563 if (GTK_WIDGET_DRAWABLE (widget
) &&
1564 GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
) {
1565 gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
),NULL
);
1570 gtk_pixmap_menu_item_expose (GtkWidget
*widget
,
1571 GdkEventExpose
*event
)
1573 g_return_val_if_fail (widget
!= NULL
, FALSE
);
1574 g_return_val_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
), FALSE
);
1575 g_return_val_if_fail (event
!= NULL
, FALSE
);
1577 if (GTK_WIDGET_CLASS (parent_class
)->expose_event
)
1578 (* GTK_WIDGET_CLASS (parent_class
)->expose_event
) (widget
, event
);
1580 if (GTK_WIDGET_DRAWABLE (widget
) &&
1581 GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
) {
1582 gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
),NULL
);
1589 * gtk_pixmap_menu_item_set_pixmap
1590 * @menu_item: Pointer to the pixmap menu item
1591 * @pixmap: Pointer to a pixmap widget
1593 * Set the pixmap of the menu item.
1598 gtk_pixmap_menu_item_set_pixmap (GtkPixmapMenuItem
*menu_item
,
1601 g_return_if_fail (menu_item
!= NULL
);
1602 g_return_if_fail (pixmap
!= NULL
);
1603 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (menu_item
));
1604 g_return_if_fail (GTK_IS_WIDGET (pixmap
));
1605 g_return_if_fail (menu_item
->pixmap
== NULL
);
1607 gtk_widget_set_parent (pixmap
, GTK_WIDGET (menu_item
));
1608 menu_item
->pixmap
= pixmap
;
1610 if (GTK_WIDGET_REALIZED (pixmap
->parent
) &&
1611 !GTK_WIDGET_REALIZED (pixmap
))
1612 gtk_widget_realize (pixmap
);
1614 if (GTK_WIDGET_VISIBLE (pixmap
->parent
)) {
1615 if (GTK_WIDGET_MAPPED (pixmap
->parent
) &&
1616 GTK_WIDGET_VISIBLE(pixmap
) &&
1617 !GTK_WIDGET_MAPPED (pixmap
))
1618 gtk_widget_map (pixmap
);
1621 changed_have_pixmap_status(menu_item
);
1623 if (GTK_WIDGET_VISIBLE (pixmap
) && GTK_WIDGET_VISIBLE (menu_item
))
1624 gtk_widget_queue_resize (pixmap
);
1628 gtk_pixmap_menu_item_map (GtkWidget
*widget
)
1630 GtkPixmapMenuItem
*menu_item
;
1632 g_return_if_fail (widget
!= NULL
);
1633 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
));
1635 menu_item
= GTK_PIXMAP_MENU_ITEM(widget
);
1637 GTK_WIDGET_CLASS(parent_class
)->map(widget
);
1639 if (menu_item
->pixmap
&&
1640 GTK_WIDGET_VISIBLE (menu_item
->pixmap
) &&
1641 !GTK_WIDGET_MAPPED (menu_item
->pixmap
))
1642 gtk_widget_map (menu_item
->pixmap
);
1646 gtk_pixmap_menu_item_size_allocate (GtkWidget
*widget
,
1647 GtkAllocation
*allocation
)
1649 GtkPixmapMenuItem
*pmenu_item
;
1651 pmenu_item
= GTK_PIXMAP_MENU_ITEM(widget
);
1653 if (pmenu_item
->pixmap
&& GTK_WIDGET_VISIBLE(pmenu_item
))
1655 GtkAllocation child_allocation
;
1658 border_width
= GTK_CONTAINER (widget
)->border_width
;
1660 child_allocation
.width
= pmenu_item
->pixmap
->requisition
.width
;
1661 child_allocation
.height
= pmenu_item
->pixmap
->requisition
.height
;
1662 child_allocation
.x
= border_width
+ BORDER_SPACING
;
1663 child_allocation
.y
= (border_width
+ BORDER_SPACING
1664 + (((allocation
->height
- child_allocation
.height
) - child_allocation
.x
)
1665 / 2)); /* center pixmaps vertically */
1666 gtk_widget_size_allocate (pmenu_item
->pixmap
, &child_allocation
);
1669 if (GTK_WIDGET_CLASS (parent_class
)->size_allocate
)
1670 GTK_WIDGET_CLASS(parent_class
)->size_allocate (widget
, allocation
);
1674 gtk_pixmap_menu_item_forall (GtkContainer
*container
,
1675 gboolean include_internals
,
1676 GtkCallback callback
,
1677 gpointer callback_data
)
1679 GtkPixmapMenuItem
*menu_item
;
1681 g_return_if_fail (container
!= NULL
);
1682 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container
));
1683 g_return_if_fail (callback
!= NULL
);
1685 menu_item
= GTK_PIXMAP_MENU_ITEM (container
);
1687 if (menu_item
->pixmap
)
1688 (* callback
) (menu_item
->pixmap
, callback_data
);
1690 GTK_CONTAINER_CLASS(parent_class
)->forall(container
,include_internals
,
1691 callback
,callback_data
);
1695 gtk_pixmap_menu_item_size_request (GtkWidget
*widget
,
1696 GtkRequisition
*requisition
)
1698 GtkPixmapMenuItem
*menu_item
;
1699 GtkRequisition req
= {0, 0};
1701 g_return_if_fail (widget
!= NULL
);
1702 g_return_if_fail (GTK_IS_MENU_ITEM (widget
));
1703 g_return_if_fail (requisition
!= NULL
);
1705 GTK_WIDGET_CLASS(parent_class
)->size_request(widget
,requisition
);
1707 menu_item
= GTK_PIXMAP_MENU_ITEM (widget
);
1709 if (menu_item
->pixmap
)
1710 gtk_widget_size_request(menu_item
->pixmap
, &req
);
1712 requisition
->height
= MAX(req
.height
+ GTK_CONTAINER(widget
)->border_width
+ BORDER_SPACING
, (unsigned int) requisition
->height
);
1713 requisition
->width
+= (req
.width
+ GTK_CONTAINER(widget
)->border_width
+ BORDER_SPACING
);
1717 gtk_pixmap_menu_item_remove (GtkContainer
*container
,
1721 gboolean widget_was_visible
;
1723 g_return_if_fail (container
!= NULL
);
1724 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container
));
1725 g_return_if_fail (child
!= NULL
);
1726 g_return_if_fail (GTK_IS_WIDGET (child
));
1728 bin
= GTK_BIN (container
);
1729 g_return_if_fail ((bin
->child
== child
||
1730 (GTK_PIXMAP_MENU_ITEM(container
)->pixmap
== child
)));
1732 widget_was_visible
= GTK_WIDGET_VISIBLE (child
);
1734 gtk_widget_unparent (child
);
1735 if (bin
->child
== child
)
1738 GTK_PIXMAP_MENU_ITEM(container
)->pixmap
= NULL
;
1739 changed_have_pixmap_status(GTK_PIXMAP_MENU_ITEM(container
));
1742 if (widget_was_visible
)
1743 gtk_widget_queue_resize (GTK_WIDGET (container
));
1747 /* important to only call this if there was actually a _change_ in pixmap == NULL */
1749 changed_have_pixmap_status (GtkPixmapMenuItem
*menu_item
)
1751 if (menu_item
->pixmap
!= NULL
) {
1752 GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
+= 1;
1754 if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
== 1) {
1755 /* Install pixmap toggle size */
1756 GTK_MENU_ITEM_GET_CLASS(menu_item
)->toggle_size
= MAX(GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->orig_toggle_size
, PMAP_WIDTH
);
1759 GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
-= 1;
1761 if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
== 0) {
1762 /* Install normal toggle size */
1763 GTK_MENU_ITEM_GET_CLASS(menu_item
)->toggle_size
= GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->orig_toggle_size
;
1767 /* Note that we actually need to do this for _all_ GtkPixmapMenuItem
1768 whenever the klass->toggle_size changes; but by doing it anytime
1769 this function is called, we get the same effect, just because of
1770 how the preferences option to show pixmaps works. Bogus, broken.
1772 if (GTK_WIDGET_VISIBLE(GTK_WIDGET(menu_item
)))
1773 gtk_widget_queue_resize(GTK_WIDGET(menu_item
));
1776 #endif // USE_MENU_BITMAPS