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 //-----------------------------------------------------------------------------
165 // activate message from GTK
166 //-----------------------------------------------------------------------------
168 static void gtk_menu_open_callback( GtkWidget
*widget
, wxMenu
*menu
)
170 if (g_isIdle
) wxapp_install_idle_handler();
172 wxMenuEvent
event( wxEVT_MENU_OPEN
, -1 );
173 event
.SetEventObject( menu
);
175 if (menu
->GetEventHandler()->ProcessEvent(event
))
178 wxWindow
*win
= menu
->GetInvokingWindow();
179 if (win
) win
->GetEventHandler()->ProcessEvent( event
);
182 //-----------------------------------------------------------------------------
184 //-----------------------------------------------------------------------------
186 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar
,wxWindow
)
188 wxMenuBar::wxMenuBar( long style
)
190 /* the parent window is known after wxFrame::SetMenu() */
191 m_needParent
= FALSE
;
193 m_invokingWindow
= (wxWindow
*) NULL
;
195 if (!PreCreation( (wxWindow
*) NULL
, wxDefaultPosition
, wxDefaultSize
) ||
196 !CreateBase( (wxWindow
*) NULL
, -1, wxDefaultPosition
, wxDefaultSize
, style
, wxDefaultValidator
, wxT("menubar") ))
198 wxFAIL_MSG( wxT("wxMenuBar creation failed") );
202 m_menus
.DeleteContents( TRUE
);
204 /* GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has. */
205 #if GTK_CHECK_VERSION(1, 2, 1)
206 m_accel
= gtk_accel_group_new();
207 m_factory
= gtk_item_factory_new( GTK_TYPE_MENU_BAR
, "<main>", m_accel
);
208 m_menubar
= gtk_item_factory_get_widget( m_factory
, "<main>" );
210 m_menubar
= gtk_menu_bar_new();
213 if (style
& wxMB_DOCKABLE
)
215 m_widget
= gtk_handle_box_new();
216 gtk_container_add( GTK_CONTAINER(m_widget
), GTK_WIDGET(m_menubar
) );
217 gtk_widget_show( GTK_WIDGET(m_menubar
) );
221 m_widget
= GTK_WIDGET(m_menubar
);
229 wxMenuBar::wxMenuBar()
231 /* the parent window is known after wxFrame::SetMenu() */
232 m_needParent
= FALSE
;
234 m_invokingWindow
= (wxWindow
*) NULL
;
236 if (!PreCreation( (wxWindow
*) NULL
, wxDefaultPosition
, wxDefaultSize
) ||
237 !CreateBase( (wxWindow
*) NULL
, -1, wxDefaultPosition
, wxDefaultSize
, 0, wxDefaultValidator
, wxT("menubar") ))
239 wxFAIL_MSG( wxT("wxMenuBar creation failed") );
243 m_menus
.DeleteContents( TRUE
);
245 /* GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has. */
246 #if GTK_CHECK_VERSION(1, 2, 1)
247 m_accel
= gtk_accel_group_new();
248 m_factory
= gtk_item_factory_new( GTK_TYPE_MENU_BAR
, "<main>", m_accel
);
249 m_menubar
= gtk_item_factory_get_widget( m_factory
, "<main>" );
251 m_menubar
= gtk_menu_bar_new();
254 m_widget
= GTK_WIDGET(m_menubar
);
261 wxMenuBar::~wxMenuBar()
263 // gtk_object_unref( GTK_OBJECT(m_factory) ); why not ?
266 static void wxMenubarUnsetInvokingWindow( wxMenu
*menu
, wxWindow
*win
)
268 menu
->SetInvokingWindow( (wxWindow
*) NULL
);
270 wxWindow
*top_frame
= win
;
271 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
272 top_frame
= top_frame
->GetParent();
274 /* support for native hot keys */
275 gtk_accel_group_detach( menu
->m_accel
, ACCEL_OBJ_CAST(top_frame
->m_widget
) );
277 wxMenuItemList::Node
*node
= menu
->GetMenuItems().GetFirst();
280 wxMenuItem
*menuitem
= node
->GetData();
281 if (menuitem
->IsSubMenu())
282 wxMenubarUnsetInvokingWindow( menuitem
->GetSubMenu(), win
);
283 node
= node
->GetNext();
287 static void wxMenubarSetInvokingWindow( wxMenu
*menu
, wxWindow
*win
)
289 menu
->SetInvokingWindow( 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 hot keys */
297 ACCEL_OBJECT
*obj
= ACCEL_OBJ_CAST(top_frame
->m_widget
);
298 if ( !g_slist_find( ACCEL_OBJECTS(menu
->m_accel
), obj
) )
299 gtk_accel_group_attach( menu
->m_accel
, obj
);
300 #endif // GTK+ 1.2.1+
302 wxMenuItemList::Node
*node
= menu
->GetMenuItems().GetFirst();
305 wxMenuItem
*menuitem
= node
->GetData();
306 if (menuitem
->IsSubMenu())
307 wxMenubarSetInvokingWindow( menuitem
->GetSubMenu(), win
);
308 node
= node
->GetNext();
312 void wxMenuBar::SetInvokingWindow( wxWindow
*win
)
314 m_invokingWindow
= win
;
315 #if GTK_CHECK_VERSION(1, 2, 1)
316 wxWindow
*top_frame
= win
;
317 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
318 top_frame
= top_frame
->GetParent();
320 /* support for native key accelerators indicated by underscroes */
321 ACCEL_OBJECT
*obj
= ACCEL_OBJ_CAST(top_frame
->m_widget
);
322 if ( !g_slist_find( ACCEL_OBJECTS(m_accel
), obj
) )
323 gtk_accel_group_attach( m_accel
, obj
);
324 #endif // GTK+ 1.2.1+
326 wxMenuList::Node
*node
= m_menus
.GetFirst();
329 wxMenu
*menu
= node
->GetData();
330 wxMenubarSetInvokingWindow( menu
, win
);
331 node
= node
->GetNext();
335 void wxMenuBar::UnsetInvokingWindow( wxWindow
*win
)
337 m_invokingWindow
= (wxWindow
*) NULL
;
338 #if GTK_CHECK_VERSION(1, 2, 1)
339 wxWindow
*top_frame
= win
;
340 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
341 top_frame
= top_frame
->GetParent();
343 // support for native key accelerators indicated by underscroes
344 gtk_accel_group_detach( m_accel
, ACCEL_OBJ_CAST(top_frame
->m_widget
) );
345 #endif // GTK+ 1.2.1+
347 wxMenuList::Node
*node
= m_menus
.GetFirst();
350 wxMenu
*menu
= node
->GetData();
351 wxMenubarUnsetInvokingWindow( menu
, win
);
352 node
= node
->GetNext();
356 bool wxMenuBar::Append( wxMenu
*menu
, const wxString
&title
)
358 if ( !wxMenuBarBase::Append( menu
, title
) )
361 return GtkAppend(menu
, title
);
364 bool wxMenuBar::GtkAppend(wxMenu
*menu
, const wxString
& title
)
366 wxString
str( wxReplaceUnderscore( title
) );
368 // This doesn't have much effect right now.
369 menu
->SetTitle( str
);
371 // GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has.
372 #if GTK_CHECK_VERSION(1, 2, 1)
375 buf
<< wxT('/') << str
.c_str();
377 // local buffer in multibyte form
379 strcpy(cbuf
, wxGTK_CONV(buf
) );
381 GtkItemFactoryEntry entry
;
382 entry
.path
= (gchar
*)cbuf
; // const_cast
383 entry
.accelerator
= (gchar
*) NULL
;
384 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
385 entry
.callback_action
= 0;
386 entry
.item_type
= (char *)"<Branch>";
388 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
389 // in order to get the pointer to the item we need the item text _without_ underscores
390 wxString tmp
= wxT("<main>/");
392 for ( pc
= str
; *pc
!= wxT('\0'); pc
++ )
394 // contrary to the common sense, we must throw out _all_ underscores,
395 // (i.e. "Hello__World" => "HelloWorld" and not "Hello_World" as we
396 // might naively think). IMHO it's a bug in GTK+ (VZ)
397 while (*pc
== wxT('_'))
401 menu
->m_owner
= gtk_item_factory_get_item( m_factory
, wxGTK_CONV( tmp
) );
402 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu
->m_owner
), menu
->m_menu
);
405 menu
->m_owner
= gtk_menu_item_new_with_label( wxGTK_CONV( str
) );
406 gtk_widget_show( menu
->m_owner
);
407 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu
->m_owner
), menu
->m_menu
);
409 gtk_menu_bar_append( GTK_MENU_BAR(m_menubar
), menu
->m_owner
);
413 gtk_signal_connect( GTK_OBJECT(menu
->m_owner
), "activate",
414 GTK_SIGNAL_FUNC(gtk_menu_open_callback
),
417 // m_invokingWindow is set after wxFrame::SetMenuBar(). This call enables
418 // addings menu later on.
419 if (m_invokingWindow
)
421 wxMenubarSetInvokingWindow( menu
, m_invokingWindow
);
423 // OPTIMISE ME: we should probably cache this, or pass it
424 // directly, but for now this is a minimal
425 // change to validate the new dynamic sizing.
426 // see (and refactor :) similar code in Remove
429 wxFrame
*frame
= wxDynamicCast( m_invokingWindow
, wxFrame
);
432 frame
->UpdateMenuBarSize();
438 bool wxMenuBar::Insert(size_t pos
, wxMenu
*menu
, const wxString
& title
)
440 if ( !wxMenuBarBase::Insert(pos
, menu
, title
) )
443 // GTK+ doesn't have a function to insert a menu using GtkItemFactory (as
444 // of version 1.2.6), so we first append the item and then change its
446 if ( !GtkAppend(menu
, title
) )
449 if (pos
+1 >= m_menus
.GetCount())
452 GtkMenuShell
*menu_shell
= GTK_MENU_SHELL(m_factory
->widget
);
453 gpointer data
= g_list_last(menu_shell
->children
)->data
;
454 menu_shell
->children
= g_list_remove(menu_shell
->children
, data
);
455 menu_shell
->children
= g_list_insert(menu_shell
->children
, data
, pos
);
460 wxMenu
*wxMenuBar::Replace(size_t pos
, wxMenu
*menu
, const wxString
& title
)
462 // remove the old item and insert a new one
463 wxMenu
*menuOld
= Remove(pos
);
464 if ( menuOld
&& !Insert(pos
, menu
, title
) )
466 return (wxMenu
*) NULL
;
469 // either Insert() succeeded or Remove() failed and menuOld is NULL
473 static wxMenu
*CopyMenu (wxMenu
*menu
)
475 wxMenu
*menucopy
= new wxMenu ();
476 wxMenuItemList::Node
*node
= menu
->GetMenuItems().GetFirst();
479 wxMenuItem
*item
= node
->GetData();
480 int itemid
= item
->GetId();
481 wxString text
= item
->GetText();
482 text
.Replace(wxT("_"), wxT("&"));
483 wxMenu
*submenu
= item
->GetSubMenu();
486 wxMenuItem
* itemcopy
= new wxMenuItem(menucopy
,
488 menu
->GetHelpString(itemid
));
489 itemcopy
->SetBitmap(item
->GetBitmap());
490 itemcopy
->SetCheckable(item
->IsCheckable());
491 menucopy
->Append(itemcopy
);
494 menucopy
->Append (itemid
, text
, CopyMenu(submenu
),
495 menu
->GetHelpString(itemid
));
497 node
= node
->GetNext();
503 wxMenu
*wxMenuBar::Remove(size_t pos
)
505 wxMenu
*menu
= wxMenuBarBase::Remove(pos
);
507 return (wxMenu
*) NULL
;
510 GtkMenuShell *menu_shell = GTK_MENU_SHELL(m_factory->widget);
512 printf( "factory entries before %d\n", (int)g_slist_length(m_factory->items) );
513 printf( "menu shell entries before %d\n", (int)g_list_length( menu_shell->children ) );
516 wxMenu
*menucopy
= CopyMenu( menu
);
518 // unparent calls unref() and that would delete the widget so we raise
519 // the ref count to 2 artificially before invoking unparent.
520 gtk_widget_ref( menu
->m_menu
);
521 gtk_widget_unparent( menu
->m_menu
);
523 gtk_widget_destroy( menu
->m_owner
);
528 printf( "factory entries after %d\n", (int)g_slist_length(m_factory->items) );
529 printf( "menu shell entries after %d\n", (int)g_list_length( menu_shell->children ) );
532 if (m_invokingWindow
)
534 // OPTIMISE ME: see comment in GtkAppend
536 wxFrame
*frame
= wxDynamicCast( m_invokingWindow
, wxFrame
);
539 frame
->UpdateMenuBarSize();
545 static int FindMenuItemRecursive( const wxMenu
*menu
, const wxString
&menuString
, const wxString
&itemString
)
547 if (wxMenuItem::GetLabelFromText(menu
->GetTitle()) == wxMenuItem::GetLabelFromText(menuString
))
549 int res
= menu
->FindItem( itemString
);
550 if (res
!= wxNOT_FOUND
)
554 wxMenuItemList::Node
*node
= menu
->GetMenuItems().GetFirst();
557 wxMenuItem
*item
= node
->GetData();
558 if (item
->IsSubMenu())
559 return FindMenuItemRecursive(item
->GetSubMenu(), menuString
, itemString
);
561 node
= node
->GetNext();
567 int wxMenuBar::FindMenuItem( const wxString
&menuString
, const wxString
&itemString
) const
569 wxMenuList::Node
*node
= m_menus
.GetFirst();
572 wxMenu
*menu
= node
->GetData();
573 int res
= FindMenuItemRecursive( menu
, menuString
, itemString
);
576 node
= node
->GetNext();
582 // Find a wxMenuItem using its id. Recurses down into sub-menus
583 static wxMenuItem
* FindMenuItemByIdRecursive(const wxMenu
* menu
, int id
)
585 wxMenuItem
* result
= menu
->FindChildItem(id
);
587 wxMenuItemList::Node
*node
= menu
->GetMenuItems().GetFirst();
588 while ( node
&& result
== NULL
)
590 wxMenuItem
*item
= node
->GetData();
591 if (item
->IsSubMenu())
593 result
= FindMenuItemByIdRecursive( item
->GetSubMenu(), id
);
595 node
= node
->GetNext();
601 wxMenuItem
* wxMenuBar::FindItem( int id
, wxMenu
**menuForItem
) const
603 wxMenuItem
* result
= 0;
604 wxMenuList::Node
*node
= m_menus
.GetFirst();
605 while (node
&& result
== 0)
607 wxMenu
*menu
= node
->GetData();
608 result
= FindMenuItemByIdRecursive( menu
, id
);
609 node
= node
->GetNext();
614 *menuForItem
= result
? result
->GetMenu() : (wxMenu
*)NULL
;
620 void wxMenuBar::EnableTop( size_t pos
, bool flag
)
622 wxMenuList::Node
*node
= m_menus
.Item( pos
);
624 wxCHECK_RET( node
, wxT("menu not found") );
626 wxMenu
* menu
= node
->GetData();
629 gtk_widget_set_sensitive( menu
->m_owner
, flag
);
632 wxString
wxMenuBar::GetLabelTop( size_t pos
) const
634 wxMenuList::Node
*node
= m_menus
.Item( pos
);
636 wxCHECK_MSG( node
, wxT("invalid"), wxT("menu not found") );
638 wxMenu
* menu
= node
->GetData();
641 wxString
text( menu
->GetTitle() );
642 for ( const wxChar
*pc
= text
.c_str(); *pc
; pc
++ )
644 if ( *pc
== wxT('_') )
646 // '_' is the escape character for GTK+
650 // don't remove ampersands '&' since if we have them in the menu title
651 // it means that they were doubled to indicate "&" instead of accelerator
659 void wxMenuBar::SetLabelTop( size_t pos
, const wxString
& label
)
661 wxMenuList::Node
*node
= m_menus
.Item( pos
);
663 wxCHECK_RET( node
, wxT("menu not found") );
665 wxMenu
* menu
= node
->GetData();
667 wxString
str( wxReplaceUnderscore( label
) );
669 menu
->SetTitle( str
);
673 GtkLabel
*label
= GTK_LABEL( GTK_BIN(menu
->m_owner
)->child
);
676 gtk_label_set( label
, wxGTK_CONV( str
) );
678 /* reparse key accel */
679 (void)gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV( str
) );
680 gtk_accel_label_refetch( GTK_ACCEL_LABEL(label
) );
685 //-----------------------------------------------------------------------------
687 //-----------------------------------------------------------------------------
689 static void gtk_menu_clicked_callback( GtkWidget
*widget
, wxMenu
*menu
)
692 wxapp_install_idle_handler();
694 int id
= menu
->FindMenuIdByMenuItem(widget
);
696 /* should find it for normal (not popup) menu */
697 wxASSERT_MSG( (id
!= -1) || (menu
->GetInvokingWindow() != NULL
),
698 _T("menu item not found in gtk_menu_clicked_callback") );
700 if (!menu
->IsEnabled(id
))
703 wxMenuItem
* item
= menu
->FindChildItem( id
);
704 wxCHECK_RET( item
, wxT("error in menu item callback") );
706 if (item
->IsCheckable())
708 bool isReallyChecked
= item
->IsChecked(),
709 isInternallyChecked
= item
->wxMenuItemBase::IsChecked();
711 // ensure that the internal state is always consistent with what is
712 // shown on the screen
713 item
->wxMenuItemBase::Check(isReallyChecked
);
715 // we must not report the events for the radio button going up nor the
716 // events resulting from the calls to wxMenuItem::Check()
717 if ( (item
->GetKind() == wxITEM_RADIO
&& !isReallyChecked
) ||
718 (isInternallyChecked
== isReallyChecked
) )
723 // the user pressed on the menu item: report the event below
726 menu
->SendEvent(id
, item
->IsCheckable() ? item
->IsChecked() : -1);
729 //-----------------------------------------------------------------------------
731 //-----------------------------------------------------------------------------
733 static void gtk_menu_hilight_callback( GtkWidget
*widget
, wxMenu
*menu
)
735 if (g_isIdle
) wxapp_install_idle_handler();
737 int id
= menu
->FindMenuIdByMenuItem(widget
);
739 wxASSERT( id
!= -1 ); // should find it!
741 if (!menu
->IsEnabled(id
))
744 wxMenuEvent
event( wxEVT_MENU_HIGHLIGHT
, id
);
745 event
.SetEventObject( menu
);
747 if (menu
->GetEventHandler()->ProcessEvent(event
))
750 wxWindow
*win
= menu
->GetInvokingWindow();
751 if (win
) win
->GetEventHandler()->ProcessEvent( event
);
754 //-----------------------------------------------------------------------------
756 //-----------------------------------------------------------------------------
758 static void gtk_menu_nolight_callback( GtkWidget
*widget
, wxMenu
*menu
)
760 if (g_isIdle
) wxapp_install_idle_handler();
762 int id
= menu
->FindMenuIdByMenuItem(widget
);
764 wxASSERT( id
!= -1 ); // should find it!
766 if (!menu
->IsEnabled(id
))
769 wxMenuEvent
event( wxEVT_MENU_HIGHLIGHT
, -1 );
770 event
.SetEventObject( menu
);
772 if (menu
->GetEventHandler()->ProcessEvent(event
))
775 wxWindow
*win
= menu
->GetInvokingWindow();
777 win
->GetEventHandler()->ProcessEvent( event
);
780 //-----------------------------------------------------------------------------
782 //-----------------------------------------------------------------------------
784 IMPLEMENT_DYNAMIC_CLASS(wxMenuItem
, wxObject
)
786 wxMenuItem
*wxMenuItemBase::New(wxMenu
*parentMenu
,
788 const wxString
& name
,
789 const wxString
& help
,
793 return new wxMenuItem(parentMenu
, id
, name
, help
, kind
, subMenu
);
796 wxMenuItem::wxMenuItem(wxMenu
*parentMenu
,
798 const wxString
& text
,
799 const wxString
& help
,
802 : wxMenuItemBase(parentMenu
, id
, text
, help
, kind
, subMenu
)
807 wxMenuItem::wxMenuItem(wxMenu
*parentMenu
,
809 const wxString
& text
,
810 const wxString
& help
,
813 : wxMenuItemBase(parentMenu
, id
, text
, help
,
814 isCheckable
? wxITEM_CHECK
: wxITEM_NORMAL
, subMenu
)
819 void wxMenuItem::Init(const wxString
& text
)
821 m_labelWidget
= (GtkWidget
*) NULL
;
822 m_menuItem
= (GtkWidget
*) NULL
;
827 wxMenuItem::~wxMenuItem()
829 // don't delete menu items, the menus take care of that
832 // return the menu item text without any menu accels
834 wxString
wxMenuItemBase::GetLabelFromText(const wxString
& text
)
838 for ( const wxChar
*pc
= text
.c_str(); *pc
; pc
++ )
840 if ( *pc
== wxT('_') )
842 // GTK 1.2 escapes "xxx_xxx" to "xxx__xxx"
848 #if GTK_CHECK_VERSION(2, 0, 0)
849 if ( *pc
== wxT('\\') )
851 // GTK 2.0 escapes "xxx/xxx" to "xxx\/xxx"
858 if ( (*pc
== wxT('&')) && (*(pc
+1) != wxT('&')) )
861 // "&" is doubled to indicate "&" instead of accelerator
868 // wxPrintf( L"text %s label %s\n", text.c_str(), label.c_str() );
873 void wxMenuItem::SetText( const wxString
& str
)
875 // Some optimization to avoid flicker
876 wxString oldLabel
= m_text
;
877 oldLabel
= wxStripMenuCodes(oldLabel
.BeforeFirst('\t'));
878 oldLabel
.Replace(wxT("_"), wxT(""));
879 wxString label1
= wxStripMenuCodes(str
.BeforeFirst('\t'));
880 if (oldLabel
== label1
)
889 label
= (GtkLabel
*) m_labelWidget
;
891 label
= GTK_LABEL( GTK_BIN(m_menuItem
)->child
);
893 #if GTK_CHECK_VERSION(2, 0, 0)
894 // We have to imitate item_factory_unescape_label here
896 for (size_t n
= 0; n
< m_text
.Len(); n
++)
898 if (m_text
[n
] != wxT('\\'))
902 gtk_label_set_text_with_mnemonic( GTK_LABEL(label
), wxGTK_CONV(tmp
) );
905 gtk_label_set( label
, wxGTK_CONV( m_text
) );
908 (void)gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV(m_text
) );
909 gtk_accel_label_refetch( GTK_ACCEL_LABEL(label
) );
914 // it's valid for this function to be called even if m_menuItem == NULL
915 void wxMenuItem::DoSetText( const wxString
& str
)
917 // '\t' is the deliminator indicating a hot key
919 const wxChar
*pc
= str
;
920 while ( (*pc
!= wxT('\0')) && (*pc
!= wxT('\t')) )
922 if ((*pc
== wxT('&')) && (*(pc
+1) == wxT('&')))
924 // "&" is doubled to indicate "&" instead of accelerator
928 else if (*pc
== wxT('&'))
932 #if GTK_CHECK_VERSION(2, 0, 0)
933 else if ( *pc
== wxT('_') ) // escape underscores
935 // m_text << wxT("__"); doesn't work
938 else if (*pc
== wxT('/')) // we have to escape slashes
940 m_text
<< wxT("\\/");
942 else if (*pc
== wxT('\\')) // we have to double backslashes
944 m_text
<< wxT("\\\\");
947 else if ( *pc
== wxT('_') ) // escape underscores
951 else if (*pc
== wxT('/')) /* we have to filter out slashes ... */
953 m_text
<< wxT('\\'); /* ... and replace them with back slashes */
962 // wxPrintf( L"str %s m_text %s\n", str.c_str(), m_text.c_str() );
975 wxAcceleratorEntry
*wxMenuItem::GetAccel() const
980 return (wxAcceleratorEntry
*)NULL
;
983 // as wxGetAccelFromString() looks for TAB, insert a dummy one here
985 label
<< wxT('\t') << GetHotKey();
987 return wxGetAccelFromString(label
);
990 #endif // wxUSE_ACCEL
992 void wxMenuItem::Check( bool check
)
994 wxCHECK_RET( m_menuItem
, wxT("invalid menu item") );
996 if (check
== m_isChecked
)
999 wxMenuItemBase::Check( check
);
1001 switch ( GetKind() )
1005 gtk_check_menu_item_set_state( (GtkCheckMenuItem
*)m_menuItem
, (gint
)check
);
1009 wxFAIL_MSG( _T("can't check this item") );
1013 void wxMenuItem::Enable( bool enable
)
1015 wxCHECK_RET( m_menuItem
, wxT("invalid menu item") );
1017 gtk_widget_set_sensitive( m_menuItem
, enable
);
1018 wxMenuItemBase::Enable( enable
);
1021 bool wxMenuItem::IsChecked() const
1023 wxCHECK_MSG( m_menuItem
, FALSE
, wxT("invalid menu item") );
1025 wxCHECK_MSG( IsCheckable(), FALSE
,
1026 wxT("can't get state of uncheckable item!") );
1028 return ((GtkCheckMenuItem
*)m_menuItem
)->active
!= 0;
1031 wxString
wxMenuItem::GetFactoryPath() const
1033 // In order to get the pointer to the item we need the item
1034 // text _without_ underscores in GTK 1.2
1035 wxString
path( wxT("<main>/") );
1037 for ( const wxChar
*pc
= m_text
.c_str(); *pc
; pc
++ )
1039 if ( *pc
== wxT('_') )
1044 // remove '_' unconditionally
1049 // don't remove ampersands '&' since if we have them in the menu item title
1050 // it means that they were doubled to indicate "&" instead of accelerator
1058 //-----------------------------------------------------------------------------
1060 //-----------------------------------------------------------------------------
1062 IMPLEMENT_DYNAMIC_CLASS(wxMenu
,wxEvtHandler
)
1066 m_accel
= gtk_accel_group_new();
1067 m_factory
= gtk_item_factory_new( GTK_TYPE_MENU
, "<main>", m_accel
);
1068 m_menu
= gtk_item_factory_get_widget( m_factory
, "<main>" );
1070 m_owner
= (GtkWidget
*) NULL
;
1072 // Tearoffs are entries, just like separators. So if we want this
1073 // menu to be a tear-off one, we just append a tearoff entry
1075 if(m_style
& wxMENU_TEAROFF
)
1077 GtkItemFactoryEntry entry
;
1078 entry
.path
= (char *)"/tearoff";
1079 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
1080 entry
.callback_action
= 0;
1081 entry
.item_type
= (char *)"<Tearoff>";
1082 entry
.accelerator
= (gchar
*) NULL
;
1083 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
1084 //GtkWidget *menuItem = gtk_item_factory_get_widget( m_factory, "<main>/tearoff" );
1087 // append the title as the very first entry if we have it
1090 Append(-2, m_title
);
1099 gtk_widget_destroy( m_menu
);
1101 gtk_object_unref( GTK_OBJECT(m_factory
) );
1104 bool wxMenu::GtkAppend(wxMenuItem
*mitem
)
1106 GtkWidget
*menuItem
;
1108 #if defined(USE_MENU_BITMAPS) || !GTK_CHECK_VERSION(1, 2, 0)
1109 bool appended
= FALSE
;
1112 // does this item terminate the current radio group?
1113 bool endOfRadioGroup
= TRUE
;
1115 if ( mitem
->IsSeparator() )
1117 GtkItemFactoryEntry entry
;
1118 entry
.path
= (char *)"/sep";
1119 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
1120 entry
.callback_action
= 0;
1121 entry
.item_type
= (char *)"<Separator>";
1122 entry
.accelerator
= (gchar
*) NULL
;
1124 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
1126 // this will be wrong for more than one separator. do we care?
1127 menuItem
= gtk_item_factory_get_widget( m_factory
, "<main>/sep" );
1129 // we might have a separator inside a radio group
1130 endOfRadioGroup
= FALSE
;
1132 else if ( mitem
->IsSubMenu() )
1134 // text has "_" instead of "&" after mitem->SetText()
1135 wxString
text( mitem
->GetText() );
1137 // local buffer in multibyte form
1140 strcat( buf
, wxGTK_CONV( text
) );
1142 GtkItemFactoryEntry entry
;
1144 entry
.callback
= (GtkItemFactoryCallback
) 0;
1145 entry
.callback_action
= 0;
1146 entry
.item_type
= (char *)"<Branch>";
1147 entry
.accelerator
= (gchar
*) NULL
;
1149 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
1151 wxString
path( mitem
->GetFactoryPath() );
1152 menuItem
= gtk_item_factory_get_item( m_factory
, wxGTK_CONV( path
) );
1154 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menuItem
), mitem
->GetSubMenu()->m_menu
);
1156 // if adding a submenu to a menu already existing in the menu bar, we
1157 // must set invoking window to allow processing events from this
1159 if ( m_invokingWindow
)
1160 wxMenubarSetInvokingWindow(mitem
->GetSubMenu(), m_invokingWindow
);
1162 #ifdef USE_MENU_BITMAPS
1163 else if (mitem
->GetBitmap().Ok()) // An item with bitmap
1165 wxString
text( mitem
->GetText() );
1166 const wxBitmap
*bitmap
= &mitem
->GetBitmap();
1168 menuItem
= gtk_pixmap_menu_item_new ();
1169 GtkWidget
*label
= gtk_accel_label_new ( wxGTK_CONV( text
) );
1170 gtk_misc_set_alignment (GTK_MISC (label
), 0.0, 0.5);
1171 gtk_container_add (GTK_CONTAINER (menuItem
), label
);
1172 guint accel_key
= gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV( text
) );
1173 gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (label
), menuItem
);
1174 if (accel_key
!= GDK_VoidSymbol
)
1176 gtk_widget_add_accelerator (menuItem
,
1178 gtk_menu_ensure_uline_accel_group (GTK_MENU (m_menu
)),
1182 gtk_widget_show (label
);
1184 mitem
->SetLabelWidget(label
);
1186 GtkWidget
* pixmap
= gtk_pixmap_new( bitmap
->GetPixmap(), bitmap
->GetMask() ? bitmap
->GetMask()->GetBitmap() : (GdkBitmap
* )NULL
);
1187 gtk_widget_show(pixmap
);
1188 gtk_pixmap_menu_item_set_pixmap(GTK_PIXMAP_MENU_ITEM( menuItem
), pixmap
);
1190 gtk_signal_connect( GTK_OBJECT(menuItem
), "activate",
1191 GTK_SIGNAL_FUNC(gtk_menu_clicked_callback
),
1194 gtk_menu_append( GTK_MENU(m_menu
), menuItem
);
1195 gtk_widget_show( menuItem
);
1197 appended
= TRUE
; // We've done this, don't do it again
1199 #endif // USE_MENU_BITMAPS
1200 else // a normal item
1202 // text has "_" instead of "&" after mitem->SetText() so don't use it
1203 wxString
text( mitem
->GetText() );
1205 // buffers containing the menu item path and type in multibyte form
1209 strcpy( bufPath
, "/" );
1210 strncat( bufPath
, wxGTK_CONV(text
), WXSIZEOF(bufPath
) - 2 );
1211 bufPath
[WXSIZEOF(bufPath
) - 1] = '\0';
1213 GtkItemFactoryEntry entry
;
1214 entry
.path
= bufPath
;
1215 entry
.callback
= (GtkItemFactoryCallback
) gtk_menu_clicked_callback
;
1216 entry
.callback_action
= 0;
1219 const char *item_type
;
1220 switch ( mitem
->GetKind() )
1223 item_type
= "<CheckItem>";
1227 if ( m_pathLastRadio
.empty() )
1229 // start of a new radio group
1230 item_type
= "<RadioItem>";
1231 wxString
tmp( wxGTK_CONV_BACK( bufPath
) );
1233 m_pathLastRadio
= tmp
;
1235 else // continue the radio group
1237 pathRadio
= m_pathLastRadio
;
1238 pathRadio
.Replace(wxT("_"), wxT(""));
1239 pathRadio
.Prepend(wxT("<main>/"));
1241 strncpy(bufType
, wxGTK_CONV(pathRadio
), WXSIZEOF(bufType
));
1242 bufType
[WXSIZEOF(bufType
) - 1] = '\0';
1243 item_type
= bufType
;
1246 // continue the existing radio group, if any
1247 endOfRadioGroup
= FALSE
;
1251 wxFAIL_MSG( _T("unexpected menu item kind") );
1255 item_type
= "<Item>";
1259 entry
.item_type
= (char *)item_type
; // cast needed for GTK+
1260 entry
.accelerator
= (gchar
*) NULL
;
1263 // due to an apparent bug in GTK+, we have to use a static buffer here -
1264 // otherwise GTK+ 1.2.2 manages to override the memory we pass to it
1266 char s_accel
[50]; // should be big enough, we check for overruns
1267 wxString
tmp( GetHotKey(*mitem
) );
1268 strncpy(s_accel
, wxGTK_CONV( tmp
), WXSIZEOF(s_accel
));
1269 s_accel
[WXSIZEOF(s_accel
) - 1] = '\0';
1270 entry
.accelerator
= s_accel
;
1271 #else // !wxUSE_ACCEL
1272 entry
.accelerator
= (char*) NULL
;
1273 #endif // wxUSE_ACCEL/!wxUSE_ACCEL
1275 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); /* what is 2 ? */
1277 wxString
path( mitem
->GetFactoryPath() );
1278 menuItem
= gtk_item_factory_get_widget( m_factory
, wxGTK_CONV( path
) );
1281 wxLogError( wxT("Wrong menu path: %s\n"), path
.c_str() );
1284 if ( !mitem
->IsSeparator() )
1286 wxASSERT_MSG( menuItem
, wxT("invalid menuitem") );
1288 gtk_signal_connect( GTK_OBJECT(menuItem
), "select",
1289 GTK_SIGNAL_FUNC(gtk_menu_hilight_callback
),
1292 gtk_signal_connect( GTK_OBJECT(menuItem
), "deselect",
1293 GTK_SIGNAL_FUNC(gtk_menu_nolight_callback
),
1297 mitem
->SetMenuItem(menuItem
);
1299 if ( endOfRadioGroup
)
1301 m_pathLastRadio
.clear();
1307 bool wxMenu::DoAppend(wxMenuItem
*mitem
)
1309 return GtkAppend(mitem
) && wxMenuBase::DoAppend(mitem
);
1312 bool wxMenu::DoInsert(size_t pos
, wxMenuItem
*item
)
1314 if ( !wxMenuBase::DoInsert(pos
, item
) )
1317 // GTK+ doesn't have a function to insert a menu using GtkItemFactory (as
1318 // of version 1.2.6), so we first append the item and then change its
1320 if ( !GtkAppend(item
) )
1323 if ( m_style
& wxMENU_TEAROFF
)
1325 // change the position as the first item is the tear-off marker
1329 GtkMenuShell
*menu_shell
= GTK_MENU_SHELL(m_factory
->widget
);
1330 gpointer data
= g_list_last(menu_shell
->children
)->data
;
1331 menu_shell
->children
= g_list_remove(menu_shell
->children
, data
);
1332 menu_shell
->children
= g_list_insert(menu_shell
->children
, data
, pos
);
1337 wxMenuItem
*wxMenu::DoRemove(wxMenuItem
*item
)
1339 if ( !wxMenuBase::DoRemove(item
) )
1340 return (wxMenuItem
*)NULL
;
1342 // TODO: this code doesn't delete the item factory item and this seems
1343 // impossible as of GTK 1.2.6.
1344 gtk_widget_destroy( item
->GetMenuItem() );
1349 int wxMenu::FindMenuIdByMenuItem( GtkWidget
*menuItem
) const
1351 wxMenuItemList::Node
*node
= m_items
.GetFirst();
1354 wxMenuItem
*item
= node
->GetData();
1355 if (item
->GetMenuItem() == menuItem
)
1356 return item
->GetId();
1357 node
= node
->GetNext();
1363 // ----------------------------------------------------------------------------
1365 // ----------------------------------------------------------------------------
1367 #if GTK_CHECK_VERSION(1, 2, 0) && wxUSE_ACCEL
1369 static wxString
GetHotKey( const wxMenuItem
& item
)
1373 wxAcceleratorEntry
*accel
= item
.GetAccel();
1376 int flags
= accel
->GetFlags();
1377 if ( flags
& wxACCEL_ALT
)
1378 hotkey
+= wxT("<alt>");
1379 if ( flags
& wxACCEL_CTRL
)
1380 hotkey
+= wxT("<control>");
1381 if ( flags
& wxACCEL_SHIFT
)
1382 hotkey
+= wxT("<shift>");
1384 int code
= accel
->GetKeyCode();
1399 hotkey
<< wxT('F') << code
- WXK_F1
+ 1;
1402 // TODO: we should use gdk_keyval_name() (a.k.a.
1403 // XKeysymToString) here as well as hardcoding the keysym
1404 // names this might be not portable
1405 case WXK_NUMPAD_INSERT
:
1406 hotkey
<< wxT("KP_Insert" );
1408 case WXK_NUMPAD_DELETE
:
1409 hotkey
<< wxT("KP_Delete" );
1412 hotkey
<< wxT("Insert" );
1415 hotkey
<< wxT("Delete" );
1418 hotkey
<< wxT("Up" );
1421 hotkey
<< wxT("Down" );
1424 hotkey
<< wxT("Prior" );
1427 hotkey
<< wxT("Next" );
1430 hotkey
<< wxT("Left" );
1433 hotkey
<< wxT("Right" );
1436 hotkey
<< wxT("Home" );
1439 hotkey
<< wxT("End" );
1442 hotkey
<< wxT("Return" );
1445 // if there are any other keys wxGetAccelFromString() may
1446 // return, we should process them here
1451 wxString name
= wxGTK_CONV_BACK( gdk_keyval_name((guint
)code
) );
1459 wxFAIL_MSG( wxT("unknown keyboard accel") );
1468 #endif // wxUSE_ACCEL
1471 //-----------------------------------------------------------------------------
1472 // substitute for missing GtkPixmapMenuItem
1473 //-----------------------------------------------------------------------------
1475 #ifdef USE_MENU_BITMAPS
1478 * Copyright (C) 1998, 1999, 2000 Free Software Foundation
1479 * All rights reserved.
1481 * This file is part of the Gnome Library.
1483 * The Gnome Library is free software; you can redistribute it and/or
1484 * modify it under the terms of the GNU Library General Public License as
1485 * published by the Free Software Foundation; either version 2 of the
1486 * License, or (at your option) any later version.
1488 * The Gnome Library is distributed in the hope that it will be useful,
1489 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1490 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1491 * Library General Public License for more details.
1493 * You should have received a copy of the GNU Library General Public
1494 * License along with the Gnome Library; see the file COPYING.LIB. If not,
1495 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
1496 * Boston, MA 02111-1307, USA.
1502 /* Author: Dietmar Maurer <dm@vlsivie.tuwien.ac.at> */
1504 #include <gtk/gtkaccellabel.h>
1505 #include <gtk/gtksignal.h>
1506 #include <gtk/gtkmenuitem.h>
1507 #include <gtk/gtkmenu.h>
1508 #include <gtk/gtkcontainer.h>
1513 static void gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass
*klass
);
1514 static void gtk_pixmap_menu_item_init (GtkPixmapMenuItem
*menu_item
);
1515 static void gtk_pixmap_menu_item_draw (GtkWidget
*widget
,
1516 GdkRectangle
*area
);
1517 static gint
gtk_pixmap_menu_item_expose (GtkWidget
*widget
,
1518 GdkEventExpose
*event
);
1520 /* we must override the following functions */
1522 static void gtk_pixmap_menu_item_map (GtkWidget
*widget
);
1523 static void gtk_pixmap_menu_item_size_allocate (GtkWidget
*widget
,
1524 GtkAllocation
*allocation
);
1525 static void gtk_pixmap_menu_item_forall (GtkContainer
*container
,
1526 gboolean include_internals
,
1527 GtkCallback callback
,
1528 gpointer callback_data
);
1529 static void gtk_pixmap_menu_item_size_request (GtkWidget
*widget
,
1530 GtkRequisition
*requisition
);
1531 static void gtk_pixmap_menu_item_remove (GtkContainer
*container
,
1534 static void changed_have_pixmap_status (GtkPixmapMenuItem
*menu_item
);
1536 static GtkMenuItemClass
*parent_class
= NULL
;
1540 #define BORDER_SPACING 3
1541 #define PMAP_WIDTH 20
1544 gtk_pixmap_menu_item_get_type (void)
1546 static GtkType pixmap_menu_item_type
= 0;
1548 if (!pixmap_menu_item_type
)
1550 GtkTypeInfo pixmap_menu_item_info
=
1552 (char *)"GtkPixmapMenuItem",
1553 sizeof (GtkPixmapMenuItem
),
1554 sizeof (GtkPixmapMenuItemClass
),
1555 (GtkClassInitFunc
) gtk_pixmap_menu_item_class_init
,
1556 (GtkObjectInitFunc
) gtk_pixmap_menu_item_init
,
1557 /* reserved_1 */ NULL
,
1558 /* reserved_2 */ NULL
,
1559 (GtkClassInitFunc
) NULL
,
1562 pixmap_menu_item_type
= gtk_type_unique (gtk_menu_item_get_type (),
1563 &pixmap_menu_item_info
);
1566 return pixmap_menu_item_type
;
1570 * gtk_pixmap_menu_item_new
1572 * Creates a new pixmap menu item. Use gtk_pixmap_menu_item_set_pixmap()
1573 * to set the pixmap wich is displayed at the left side.
1576 * &GtkWidget pointer to new menu item
1580 gtk_pixmap_menu_item_new (void)
1582 return GTK_WIDGET (gtk_type_new (gtk_pixmap_menu_item_get_type ()));
1586 gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass
*klass
)
1588 GtkObjectClass
*object_class
;
1589 GtkWidgetClass
*widget_class
;
1590 GtkMenuItemClass
*menu_item_class
;
1591 GtkContainerClass
*container_class
;
1593 object_class
= (GtkObjectClass
*) klass
;
1594 widget_class
= (GtkWidgetClass
*) klass
;
1595 menu_item_class
= (GtkMenuItemClass
*) klass
;
1596 container_class
= (GtkContainerClass
*) klass
;
1598 parent_class
= (GtkMenuItemClass
*) gtk_type_class (gtk_menu_item_get_type ());
1600 widget_class
->draw
= gtk_pixmap_menu_item_draw
;
1601 widget_class
->expose_event
= gtk_pixmap_menu_item_expose
;
1602 widget_class
->map
= gtk_pixmap_menu_item_map
;
1603 widget_class
->size_allocate
= gtk_pixmap_menu_item_size_allocate
;
1604 widget_class
->size_request
= gtk_pixmap_menu_item_size_request
;
1606 container_class
->forall
= gtk_pixmap_menu_item_forall
;
1607 container_class
->remove
= gtk_pixmap_menu_item_remove
;
1609 klass
->orig_toggle_size
= menu_item_class
->toggle_size
;
1610 klass
->have_pixmap_count
= 0;
1614 gtk_pixmap_menu_item_init (GtkPixmapMenuItem
*menu_item
)
1618 mi
= GTK_MENU_ITEM (menu_item
);
1620 menu_item
->pixmap
= NULL
;
1624 gtk_pixmap_menu_item_draw (GtkWidget
*widget
,
1627 g_return_if_fail (widget
!= NULL
);
1628 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
));
1629 g_return_if_fail (area
!= NULL
);
1631 if (GTK_WIDGET_CLASS (parent_class
)->draw
)
1632 (* GTK_WIDGET_CLASS (parent_class
)->draw
) (widget
, area
);
1634 if (GTK_WIDGET_DRAWABLE (widget
) &&
1635 GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
) {
1636 gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
),NULL
);
1641 gtk_pixmap_menu_item_expose (GtkWidget
*widget
,
1642 GdkEventExpose
*event
)
1644 g_return_val_if_fail (widget
!= NULL
, FALSE
);
1645 g_return_val_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
), FALSE
);
1646 g_return_val_if_fail (event
!= NULL
, FALSE
);
1648 if (GTK_WIDGET_CLASS (parent_class
)->expose_event
)
1649 (* GTK_WIDGET_CLASS (parent_class
)->expose_event
) (widget
, event
);
1651 if (GTK_WIDGET_DRAWABLE (widget
) &&
1652 GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
) {
1653 gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
),NULL
);
1660 * gtk_pixmap_menu_item_set_pixmap
1661 * @menu_item: Pointer to the pixmap menu item
1662 * @pixmap: Pointer to a pixmap widget
1664 * Set the pixmap of the menu item.
1669 gtk_pixmap_menu_item_set_pixmap (GtkPixmapMenuItem
*menu_item
,
1672 g_return_if_fail (menu_item
!= NULL
);
1673 g_return_if_fail (pixmap
!= NULL
);
1674 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (menu_item
));
1675 g_return_if_fail (GTK_IS_WIDGET (pixmap
));
1676 g_return_if_fail (menu_item
->pixmap
== NULL
);
1678 gtk_widget_set_parent (pixmap
, GTK_WIDGET (menu_item
));
1679 menu_item
->pixmap
= pixmap
;
1681 if (GTK_WIDGET_REALIZED (pixmap
->parent
) &&
1682 !GTK_WIDGET_REALIZED (pixmap
))
1683 gtk_widget_realize (pixmap
);
1685 if (GTK_WIDGET_VISIBLE (pixmap
->parent
)) {
1686 if (GTK_WIDGET_MAPPED (pixmap
->parent
) &&
1687 GTK_WIDGET_VISIBLE(pixmap
) &&
1688 !GTK_WIDGET_MAPPED (pixmap
))
1689 gtk_widget_map (pixmap
);
1692 changed_have_pixmap_status(menu_item
);
1694 if (GTK_WIDGET_VISIBLE (pixmap
) && GTK_WIDGET_VISIBLE (menu_item
))
1695 gtk_widget_queue_resize (pixmap
);
1699 gtk_pixmap_menu_item_map (GtkWidget
*widget
)
1701 GtkPixmapMenuItem
*menu_item
;
1703 g_return_if_fail (widget
!= NULL
);
1704 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
));
1706 menu_item
= GTK_PIXMAP_MENU_ITEM(widget
);
1708 GTK_WIDGET_CLASS(parent_class
)->map(widget
);
1710 if (menu_item
->pixmap
&&
1711 GTK_WIDGET_VISIBLE (menu_item
->pixmap
) &&
1712 !GTK_WIDGET_MAPPED (menu_item
->pixmap
))
1713 gtk_widget_map (menu_item
->pixmap
);
1717 gtk_pixmap_menu_item_size_allocate (GtkWidget
*widget
,
1718 GtkAllocation
*allocation
)
1720 GtkPixmapMenuItem
*pmenu_item
;
1722 pmenu_item
= GTK_PIXMAP_MENU_ITEM(widget
);
1724 if (pmenu_item
->pixmap
&& GTK_WIDGET_VISIBLE(pmenu_item
))
1726 GtkAllocation child_allocation
;
1729 border_width
= GTK_CONTAINER (widget
)->border_width
;
1731 child_allocation
.width
= pmenu_item
->pixmap
->requisition
.width
;
1732 child_allocation
.height
= pmenu_item
->pixmap
->requisition
.height
;
1733 child_allocation
.x
= border_width
+ BORDER_SPACING
;
1734 child_allocation
.y
= (border_width
+ BORDER_SPACING
1735 + (((allocation
->height
- child_allocation
.height
) - child_allocation
.x
)
1736 / 2)); /* center pixmaps vertically */
1737 gtk_widget_size_allocate (pmenu_item
->pixmap
, &child_allocation
);
1740 if (GTK_WIDGET_CLASS (parent_class
)->size_allocate
)
1741 GTK_WIDGET_CLASS(parent_class
)->size_allocate (widget
, allocation
);
1745 gtk_pixmap_menu_item_forall (GtkContainer
*container
,
1746 gboolean include_internals
,
1747 GtkCallback callback
,
1748 gpointer callback_data
)
1750 GtkPixmapMenuItem
*menu_item
;
1752 g_return_if_fail (container
!= NULL
);
1753 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container
));
1754 g_return_if_fail (callback
!= NULL
);
1756 menu_item
= GTK_PIXMAP_MENU_ITEM (container
);
1758 if (menu_item
->pixmap
)
1759 (* callback
) (menu_item
->pixmap
, callback_data
);
1761 GTK_CONTAINER_CLASS(parent_class
)->forall(container
,include_internals
,
1762 callback
,callback_data
);
1766 gtk_pixmap_menu_item_size_request (GtkWidget
*widget
,
1767 GtkRequisition
*requisition
)
1769 GtkPixmapMenuItem
*menu_item
;
1770 GtkRequisition req
= {0, 0};
1772 g_return_if_fail (widget
!= NULL
);
1773 g_return_if_fail (GTK_IS_MENU_ITEM (widget
));
1774 g_return_if_fail (requisition
!= NULL
);
1776 GTK_WIDGET_CLASS(parent_class
)->size_request(widget
,requisition
);
1778 menu_item
= GTK_PIXMAP_MENU_ITEM (widget
);
1780 if (menu_item
->pixmap
)
1781 gtk_widget_size_request(menu_item
->pixmap
, &req
);
1783 requisition
->height
= MAX(req
.height
+ GTK_CONTAINER(widget
)->border_width
+ BORDER_SPACING
, (unsigned int) requisition
->height
);
1784 requisition
->width
+= (req
.width
+ GTK_CONTAINER(widget
)->border_width
+ BORDER_SPACING
);
1788 gtk_pixmap_menu_item_remove (GtkContainer
*container
,
1792 gboolean widget_was_visible
;
1794 g_return_if_fail (container
!= NULL
);
1795 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container
));
1796 g_return_if_fail (child
!= NULL
);
1797 g_return_if_fail (GTK_IS_WIDGET (child
));
1799 bin
= GTK_BIN (container
);
1800 g_return_if_fail ((bin
->child
== child
||
1801 (GTK_PIXMAP_MENU_ITEM(container
)->pixmap
== child
)));
1803 widget_was_visible
= GTK_WIDGET_VISIBLE (child
);
1805 gtk_widget_unparent (child
);
1806 if (bin
->child
== child
)
1809 GTK_PIXMAP_MENU_ITEM(container
)->pixmap
= NULL
;
1810 changed_have_pixmap_status(GTK_PIXMAP_MENU_ITEM(container
));
1813 if (widget_was_visible
)
1814 gtk_widget_queue_resize (GTK_WIDGET (container
));
1818 /* important to only call this if there was actually a _change_ in pixmap == NULL */
1820 changed_have_pixmap_status (GtkPixmapMenuItem
*menu_item
)
1822 if (menu_item
->pixmap
!= NULL
) {
1823 GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
+= 1;
1825 if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
== 1) {
1826 /* Install pixmap toggle size */
1827 GTK_MENU_ITEM_GET_CLASS(menu_item
)->toggle_size
= MAX(GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->orig_toggle_size
, PMAP_WIDTH
);
1830 GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
-= 1;
1832 if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
== 0) {
1833 /* Install normal toggle size */
1834 GTK_MENU_ITEM_GET_CLASS(menu_item
)->toggle_size
= GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->orig_toggle_size
;
1838 /* Note that we actually need to do this for _all_ GtkPixmapMenuItem
1839 whenever the klass->toggle_size changes; but by doing it anytime
1840 this function is called, we get the same effect, just because of
1841 how the preferences option to show pixmaps works. Bogus, broken.
1843 if (GTK_WIDGET_VISIBLE(GTK_WIDGET(menu_item
)))
1844 gtk_widget_queue_resize(GTK_WIDGET(menu_item
));
1847 #endif // USE_MENU_BITMAPS