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+
594 // don't remove ampersands '&' since if we have them in the menu title
595 // it means that they were doubled to indicate "&" instead of accelerator
603 void wxMenuBar::SetLabelTop( size_t pos
, const wxString
& label
)
605 wxMenuList::Node
*node
= m_menus
.Item( pos
);
607 wxCHECK_RET( node
, wxT("menu not found") );
609 wxMenu
* menu
= node
->GetData();
611 wxString
str( wxReplaceUnderscore( label
) );
613 menu
->SetTitle( str
);
617 GtkLabel
*label
= GTK_LABEL( GTK_BIN(menu
->m_owner
)->child
);
620 gtk_label_set( label
, wxGTK_CONV( str
) );
622 /* reparse key accel */
623 (void)gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV( str
) );
624 gtk_accel_label_refetch( GTK_ACCEL_LABEL(label
) );
629 //-----------------------------------------------------------------------------
631 //-----------------------------------------------------------------------------
633 static void gtk_menu_clicked_callback( GtkWidget
*widget
, wxMenu
*menu
)
636 wxapp_install_idle_handler();
638 int id
= menu
->FindMenuIdByMenuItem(widget
);
640 /* should find it for normal (not popup) menu */
641 wxASSERT_MSG( (id
!= -1) || (menu
->GetInvokingWindow() != NULL
),
642 _T("menu item not found in gtk_menu_clicked_callback") );
644 if (!menu
->IsEnabled(id
))
647 wxMenuItem
* item
= menu
->FindChildItem( id
);
648 wxCHECK_RET( item
, wxT("error in menu item callback") );
650 if (item
->IsCheckable())
652 bool isReallyChecked
= item
->IsChecked(),
653 isInternallyChecked
= item
->wxMenuItemBase::IsChecked();
655 // ensure that the internal state is always consistent with what is
656 // shown on the screen
657 item
->wxMenuItemBase::Check(isReallyChecked
);
659 // we must not report the events for the radio button going up nor the
660 // events resulting from the calls to wxMenuItem::Check()
661 if ( (item
->GetKind() == wxITEM_RADIO
&& !isReallyChecked
) ||
662 (isInternallyChecked
== isReallyChecked
) )
667 // the user pressed on the menu item: report the event below
670 menu
->SendEvent(id
, item
->IsCheckable() ? item
->IsChecked() : -1);
673 //-----------------------------------------------------------------------------
675 //-----------------------------------------------------------------------------
677 static void gtk_menu_hilight_callback( GtkWidget
*widget
, wxMenu
*menu
)
679 if (g_isIdle
) wxapp_install_idle_handler();
681 int id
= menu
->FindMenuIdByMenuItem(widget
);
683 wxASSERT( id
!= -1 ); // should find it!
685 if (!menu
->IsEnabled(id
))
688 wxMenuEvent
event( wxEVT_MENU_HIGHLIGHT
, id
);
689 event
.SetEventObject( menu
);
691 if (menu
->GetEventHandler()->ProcessEvent(event
))
694 wxWindow
*win
= menu
->GetInvokingWindow();
695 if (win
) win
->GetEventHandler()->ProcessEvent( event
);
698 //-----------------------------------------------------------------------------
700 //-----------------------------------------------------------------------------
702 static void gtk_menu_nolight_callback( GtkWidget
*widget
, wxMenu
*menu
)
704 if (g_isIdle
) wxapp_install_idle_handler();
706 int id
= menu
->FindMenuIdByMenuItem(widget
);
708 wxASSERT( id
!= -1 ); // should find it!
710 if (!menu
->IsEnabled(id
))
713 wxMenuEvent
event( wxEVT_MENU_HIGHLIGHT
, -1 );
714 event
.SetEventObject( menu
);
716 if (menu
->GetEventHandler()->ProcessEvent(event
))
719 wxWindow
*win
= menu
->GetInvokingWindow();
721 win
->GetEventHandler()->ProcessEvent( event
);
724 //-----------------------------------------------------------------------------
726 //-----------------------------------------------------------------------------
728 IMPLEMENT_DYNAMIC_CLASS(wxMenuItem
, wxObject
)
730 wxMenuItem
*wxMenuItemBase::New(wxMenu
*parentMenu
,
732 const wxString
& name
,
733 const wxString
& help
,
737 return new wxMenuItem(parentMenu
, id
, name
, help
, kind
, subMenu
);
740 wxMenuItem::wxMenuItem(wxMenu
*parentMenu
,
742 const wxString
& text
,
743 const wxString
& help
,
746 : wxMenuItemBase(parentMenu
, id
, text
, help
, kind
, subMenu
)
751 wxMenuItem::wxMenuItem(wxMenu
*parentMenu
,
753 const wxString
& text
,
754 const wxString
& help
,
757 : wxMenuItemBase(parentMenu
, id
, text
, help
,
758 isCheckable
? wxITEM_CHECK
: wxITEM_NORMAL
, subMenu
)
763 void wxMenuItem::Init(const wxString
& text
)
765 m_labelWidget
= (GtkWidget
*) NULL
;
766 m_menuItem
= (GtkWidget
*) NULL
;
771 wxMenuItem::~wxMenuItem()
773 // don't delete menu items, the menus take care of that
776 // return the menu item text without any menu accels
778 wxString
wxMenuItemBase::GetLabelFromText(const wxString
& text
)
782 for ( const wxChar
*pc
= text
.c_str(); *pc
; pc
++ )
784 if ( *pc
== wxT('_') )
786 // GTK 1.2 escapes "xxx_xxx" to "xxx__xxx"
792 #if GTK_CHECK_VERSION(2, 0, 0)
793 if ( *pc
== wxT('\\') )
795 // GTK 2.0 escapes "xxx/xxx" to "xxx\/xxx"
802 if ( (*pc
== wxT('&')) && (*(pc
+1) != wxT('&')) )
805 // "&" is doubled to indicate "&" instead of accelerator
814 void wxMenuItem::SetText( const wxString
& str
)
816 // Some optimization to avoid flicker
817 wxString oldLabel
= m_text
;
818 oldLabel
= wxStripMenuCodes(oldLabel
.BeforeFirst('\t'));
819 oldLabel
.Replace(wxT("_"), wxT(""));
820 wxString label1
= wxStripMenuCodes(str
.BeforeFirst('\t'));
821 if (oldLabel
== label1
)
830 label
= (GtkLabel
*) m_labelWidget
;
832 label
= GTK_LABEL( GTK_BIN(m_menuItem
)->child
);
834 #if GTK_CHECK_VERSION(2, 0, 0)
835 // We have to imitate item_factory_unescape_label here
837 for (size_t n
= 0; n
< m_text
.Len(); n
++)
839 if (m_text
[n
] != wxT('\\'))
843 gtk_label_set_text_with_mnemonic( GTK_LABEL(label
), wxGTK_CONV(tmp
) );
846 gtk_label_set( label
, wxGTK_CONV( m_text
) );
849 (void)gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV(m_text
) );
850 gtk_accel_label_refetch( GTK_ACCEL_LABEL(label
) );
855 // it's valid for this function to be called even if m_menuItem == NULL
856 void wxMenuItem::DoSetText( const wxString
& str
)
858 // '\t' is the deliminator indicating a hot key
860 const wxChar
*pc
= str
;
861 while ( (*pc
!= wxT('\0')) && (*pc
!= wxT('\t')) )
863 if ((*pc
== wxT('&')) && (*(pc
+1) == wxT('&')))
865 // "&" is doubled to indicate "&" instead of accelerator
869 else if (*pc
== wxT('&'))
873 #if GTK_CHECK_VERSION(2, 0, 0)
874 else if ( *pc
== wxT('_') ) // escape underscores
876 // m_text << wxT("__"); doesn't work
878 else if (*pc
== wxT('/')) // we have to escape slashes
880 m_text
<< wxT("\\/");
882 else if (*pc
== wxT('\\')) // we have to double backslashes
884 m_text
<< wxT("\\\\");
887 else if ( *pc
== wxT('_') ) // escape underscores
891 else if (*pc
== wxT('/')) /* we have to filter out slashes ... */
893 m_text
<< wxT('\\'); /* ... and replace them with back slashes */
913 wxAcceleratorEntry
*wxMenuItem::GetAccel() const
918 return (wxAcceleratorEntry
*)NULL
;
921 // as wxGetAccelFromString() looks for TAB, insert a dummy one here
923 label
<< wxT('\t') << GetHotKey();
925 return wxGetAccelFromString(label
);
928 #endif // wxUSE_ACCEL
930 void wxMenuItem::Check( bool check
)
932 wxCHECK_RET( m_menuItem
, wxT("invalid menu item") );
934 if (check
== m_isChecked
)
937 wxMenuItemBase::Check( check
);
943 gtk_check_menu_item_set_state( (GtkCheckMenuItem
*)m_menuItem
, (gint
)check
);
947 wxFAIL_MSG( _T("can't check this item") );
951 void wxMenuItem::Enable( bool enable
)
953 wxCHECK_RET( m_menuItem
, wxT("invalid menu item") );
955 gtk_widget_set_sensitive( m_menuItem
, enable
);
956 wxMenuItemBase::Enable( enable
);
959 bool wxMenuItem::IsChecked() const
961 wxCHECK_MSG( m_menuItem
, FALSE
, wxT("invalid menu item") );
963 wxCHECK_MSG( IsCheckable(), FALSE
,
964 wxT("can't get state of uncheckable item!") );
966 return ((GtkCheckMenuItem
*)m_menuItem
)->active
!= 0;
969 wxString
wxMenuItem::GetFactoryPath() const
971 /* in order to get the pointer to the item we need the item text
972 _without_ underscores */
973 wxString
path( wxT("<main>/") );
975 for ( const wxChar
*pc
= m_text
.c_str(); *pc
; pc
++ )
977 if ( *pc
== wxT('_') )
979 // remove '_' unconditionally
983 // don't remove ampersands '&' since if we have them in the menu item title
984 // it means that they were doubled to indicate "&" instead of accelerator
992 //-----------------------------------------------------------------------------
994 //-----------------------------------------------------------------------------
996 IMPLEMENT_DYNAMIC_CLASS(wxMenu
,wxEvtHandler
)
1000 m_accel
= gtk_accel_group_new();
1001 m_factory
= gtk_item_factory_new( GTK_TYPE_MENU
, "<main>", m_accel
);
1002 m_menu
= gtk_item_factory_get_widget( m_factory
, "<main>" );
1004 m_owner
= (GtkWidget
*) NULL
;
1006 /* Tearoffs are entries, just like separators. So if we want this
1007 menu to be a tear-off one, we just append a tearoff entry
1009 if(m_style
& wxMENU_TEAROFF
)
1011 GtkItemFactoryEntry entry
;
1012 entry
.path
= (char *)"/tearoff";
1013 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
1014 entry
.callback_action
= 0;
1015 entry
.item_type
= (char *)"<Tearoff>";
1016 entry
.accelerator
= (gchar
*) NULL
;
1017 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); /* what is 2 ? */
1018 //GtkWidget *menuItem = gtk_item_factory_get_widget( m_factory, "<main>/tearoff" );
1021 // append the title as the very first entry if we have it
1024 Append(-2, m_title
);
1033 gtk_widget_destroy( m_menu
);
1035 gtk_object_unref( GTK_OBJECT(m_factory
) );
1038 bool wxMenu::GtkAppend(wxMenuItem
*mitem
)
1040 GtkWidget
*menuItem
;
1042 #if defined(USE_MENU_BITMAPS) || !GTK_CHECK_VERSION(1, 2, 0)
1043 bool appended
= FALSE
;
1046 // does this item terminate the current radio group?
1047 bool endOfRadioGroup
= TRUE
;
1049 if ( mitem
->IsSeparator() )
1051 GtkItemFactoryEntry entry
;
1052 entry
.path
= (char *)"/sep";
1053 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
1054 entry
.callback_action
= 0;
1055 entry
.item_type
= (char *)"<Separator>";
1056 entry
.accelerator
= (gchar
*) NULL
;
1058 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); /* what is 2 ? */
1060 /* this will be wrong for more than one separator. do we care? */
1061 menuItem
= gtk_item_factory_get_widget( m_factory
, "<main>/sep" );
1063 // we might have a separator inside a radio group
1064 endOfRadioGroup
= FALSE
;
1066 else if ( mitem
->IsSubMenu() )
1068 /* text has "_" instead of "&" after mitem->SetText() */
1069 wxString
text( mitem
->GetText() );
1071 /* local buffer in multibyte form */
1074 strcat( buf
, wxGTK_CONV( text
) );
1076 GtkItemFactoryEntry entry
;
1078 entry
.callback
= (GtkItemFactoryCallback
) 0;
1079 entry
.callback_action
= 0;
1080 entry
.item_type
= (char *)"<Branch>";
1081 entry
.accelerator
= (gchar
*) NULL
;
1083 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); /* what is 2 ? */
1085 wxString
path( mitem
->GetFactoryPath() );
1086 menuItem
= gtk_item_factory_get_item( m_factory
, wxGTK_CONV( path
) );
1088 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menuItem
), mitem
->GetSubMenu()->m_menu
);
1090 // if adding a submenu to a menu already existing in the menu bar, we
1091 // must set invoking window to allow processing events from this
1093 if ( m_invokingWindow
)
1094 wxMenubarSetInvokingWindow(mitem
->GetSubMenu(), m_invokingWindow
);
1096 #ifdef USE_MENU_BITMAPS
1097 else if (mitem
->GetBitmap().Ok()) // An item with bitmap
1099 wxString
text( mitem
->GetText() );
1100 const wxBitmap
*bitmap
= &mitem
->GetBitmap();
1102 menuItem
= gtk_pixmap_menu_item_new ();
1103 GtkWidget
*label
= gtk_accel_label_new ( wxGTK_CONV( text
) );
1104 gtk_misc_set_alignment (GTK_MISC (label
), 0.0, 0.5);
1105 gtk_container_add (GTK_CONTAINER (menuItem
), label
);
1106 guint accel_key
= gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV( text
) );
1107 gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (label
), menuItem
);
1108 if (accel_key
!= GDK_VoidSymbol
)
1110 gtk_widget_add_accelerator (menuItem
,
1112 gtk_menu_ensure_uline_accel_group (GTK_MENU (m_menu
)),
1116 gtk_widget_show (label
);
1118 mitem
->SetLabelWidget(label
);
1120 GtkWidget
* pixmap
= gtk_pixmap_new( bitmap
->GetPixmap(), bitmap
->GetMask() ? bitmap
->GetMask()->GetBitmap() : (GdkBitmap
* )NULL
);
1121 gtk_widget_show(pixmap
);
1122 gtk_pixmap_menu_item_set_pixmap(GTK_PIXMAP_MENU_ITEM( menuItem
), pixmap
);
1124 gtk_signal_connect( GTK_OBJECT(menuItem
), "activate",
1125 GTK_SIGNAL_FUNC(gtk_menu_clicked_callback
),
1128 gtk_menu_append( GTK_MENU(m_menu
), menuItem
);
1129 gtk_widget_show( menuItem
);
1131 appended
= TRUE
; // We've done this, don't do it again
1133 #endif // USE_MENU_BITMAPS
1134 else // a normal item
1136 // text has "_" instead of "&" after mitem->SetText() so don't use it
1137 wxString
text( mitem
->GetText() );
1139 // buffers containing the menu item path and type in multibyte form
1143 strcpy( bufPath
, "/" );
1144 strncat( bufPath
, wxGTK_CONV(text
), WXSIZEOF(bufPath
) - 2 );
1145 bufPath
[WXSIZEOF(bufPath
) - 1] = '\0';
1147 GtkItemFactoryEntry entry
;
1148 entry
.path
= bufPath
;
1149 entry
.callback
= (GtkItemFactoryCallback
) gtk_menu_clicked_callback
;
1150 entry
.callback_action
= 0;
1153 const char *item_type
;
1154 switch ( mitem
->GetKind() )
1157 item_type
= "<CheckItem>";
1161 if ( m_pathLastRadio
.empty() )
1163 // start of a new radio group
1164 item_type
= "<RadioItem>";
1165 wxString
tmp( wxGTK_CONV_BACK( bufPath
) );
1167 m_pathLastRadio
= tmp
;
1169 else // continue the radio group
1171 pathRadio
= m_pathLastRadio
;
1172 pathRadio
.Replace(wxT("_"), wxT(""));
1173 pathRadio
.Prepend(wxT("<main>/"));
1175 strncpy(bufType
, wxGTK_CONV(pathRadio
), WXSIZEOF(bufType
));
1176 bufType
[WXSIZEOF(bufType
) - 1] = '\0';
1177 item_type
= bufType
;
1180 // continue the existing radio group, if any
1181 endOfRadioGroup
= FALSE
;
1185 wxFAIL_MSG( _T("unexpected menu item kind") );
1189 item_type
= "<Item>";
1193 entry
.item_type
= (char *)item_type
; // cast needed for GTK+
1194 entry
.accelerator
= (gchar
*) NULL
;
1197 // due to an apparent bug in GTK+, we have to use a static buffer here -
1198 // otherwise GTK+ 1.2.2 manages to override the memory we pass to it
1200 char s_accel
[50]; // should be big enough, we check for overruns
1201 wxString
tmp( GetHotKey(*mitem
) );
1202 strncpy(s_accel
, wxGTK_CONV( tmp
), WXSIZEOF(s_accel
));
1203 s_accel
[WXSIZEOF(s_accel
) - 1] = '\0';
1204 entry
.accelerator
= s_accel
;
1205 #else // !wxUSE_ACCEL
1206 entry
.accelerator
= (char*) NULL
;
1207 #endif // wxUSE_ACCEL/!wxUSE_ACCEL
1209 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); /* what is 2 ? */
1211 wxString
path( mitem
->GetFactoryPath() );
1212 menuItem
= gtk_item_factory_get_widget( m_factory
, wxGTK_CONV( path
) );
1215 wxLogError( wxT("Wrong menu path: %s\n"), path
.c_str() );
1218 if ( !mitem
->IsSeparator() )
1220 wxASSERT_MSG( menuItem
, wxT("invalid menuitem") );
1222 gtk_signal_connect( GTK_OBJECT(menuItem
), "select",
1223 GTK_SIGNAL_FUNC(gtk_menu_hilight_callback
),
1226 gtk_signal_connect( GTK_OBJECT(menuItem
), "deselect",
1227 GTK_SIGNAL_FUNC(gtk_menu_nolight_callback
),
1231 mitem
->SetMenuItem(menuItem
);
1233 if ( endOfRadioGroup
)
1235 m_pathLastRadio
.clear();
1241 bool wxMenu::DoAppend(wxMenuItem
*mitem
)
1243 return GtkAppend(mitem
) && wxMenuBase::DoAppend(mitem
);
1246 bool wxMenu::DoInsert(size_t pos
, wxMenuItem
*item
)
1248 if ( !wxMenuBase::DoInsert(pos
, item
) )
1252 // GTK+ doesn't have a function to insert a menu using GtkItemFactory (as
1253 // of version 1.2.6), so we first append the item and then change its
1255 if ( !GtkAppend(item
) )
1258 if ( m_style
& wxMENU_TEAROFF
)
1260 // change the position as the first item is the tear-off marker
1264 GtkMenuShell
*menu_shell
= GTK_MENU_SHELL(m_factory
->widget
);
1265 gpointer data
= g_list_last(menu_shell
->children
)->data
;
1266 menu_shell
->children
= g_list_remove(menu_shell
->children
, data
);
1267 menu_shell
->children
= g_list_insert(menu_shell
->children
, data
, pos
);
1271 // this should be easy to do...
1272 wxFAIL_MSG( wxT("not implemented") );
1275 #endif // GTK 1.2/1.0
1278 wxMenuItem
*wxMenu::DoRemove(wxMenuItem
*item
)
1280 if ( !wxMenuBase::DoRemove(item
) )
1281 return (wxMenuItem
*)NULL
;
1283 // TODO: this code doesn't delete the item factory item and this seems
1284 // impossible as of GTK 1.2.6.
1285 gtk_widget_destroy( item
->GetMenuItem() );
1290 int wxMenu::FindMenuIdByMenuItem( GtkWidget
*menuItem
) const
1292 wxNode
*node
= m_items
.First();
1295 wxMenuItem
*item
= (wxMenuItem
*)node
->Data();
1296 if (item
->GetMenuItem() == menuItem
)
1297 return item
->GetId();
1298 node
= node
->Next();
1304 // ----------------------------------------------------------------------------
1306 // ----------------------------------------------------------------------------
1308 #if GTK_CHECK_VERSION(1, 2, 0) && wxUSE_ACCEL
1310 static wxString
GetHotKey( const wxMenuItem
& item
)
1314 wxAcceleratorEntry
*accel
= item
.GetAccel();
1317 int flags
= accel
->GetFlags();
1318 if ( flags
& wxACCEL_ALT
)
1319 hotkey
+= wxT("<alt>");
1320 if ( flags
& wxACCEL_CTRL
)
1321 hotkey
+= wxT("<control>");
1322 if ( flags
& wxACCEL_SHIFT
)
1323 hotkey
+= wxT("<shift>");
1325 int code
= accel
->GetKeyCode();
1340 hotkey
<< wxT('F') << code
- WXK_F1
+ 1;
1343 // TODO: we should use gdk_keyval_name() (a.k.a.
1344 // XKeysymToString) here as well as hardcoding the keysym
1345 // names this might be not portable
1346 case WXK_NUMPAD_INSERT
:
1347 hotkey
<< wxT("KP_Insert" );
1349 case WXK_NUMPAD_DELETE
:
1350 hotkey
<< wxT("KP_Delete" );
1353 hotkey
<< wxT("Insert" );
1356 hotkey
<< wxT("Delete" );
1359 hotkey
<< wxT("Up" );
1362 hotkey
<< wxT("Down" );
1365 hotkey
<< wxT("Prior" );
1368 hotkey
<< wxT("Next" );
1371 hotkey
<< wxT("Left" );
1374 hotkey
<< wxT("Right" );
1377 hotkey
<< wxT("Home" );
1380 hotkey
<< wxT("End" );
1383 hotkey
<< wxT("Return" );
1386 // if there are any other keys wxGetAccelFromString() may
1387 // return, we should process them here
1392 wxString name
= wxGTK_CONV_BACK( gdk_keyval_name((guint
)code
) );
1400 wxFAIL_MSG( wxT("unknown keyboard accel") );
1409 #endif // wxUSE_ACCEL
1412 //-----------------------------------------------------------------------------
1413 // substitute for missing GtkPixmapMenuItem
1414 //-----------------------------------------------------------------------------
1416 #ifdef USE_MENU_BITMAPS
1419 * Copyright (C) 1998, 1999, 2000 Free Software Foundation
1420 * All rights reserved.
1422 * This file is part of the Gnome Library.
1424 * The Gnome Library is free software; you can redistribute it and/or
1425 * modify it under the terms of the GNU Library General Public License as
1426 * published by the Free Software Foundation; either version 2 of the
1427 * License, or (at your option) any later version.
1429 * The Gnome Library is distributed in the hope that it will be useful,
1430 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1431 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1432 * Library General Public License for more details.
1434 * You should have received a copy of the GNU Library General Public
1435 * License along with the Gnome Library; see the file COPYING.LIB. If not,
1436 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
1437 * Boston, MA 02111-1307, USA.
1443 /* Author: Dietmar Maurer <dm@vlsivie.tuwien.ac.at> */
1445 #include <gtk/gtkaccellabel.h>
1446 #include <gtk/gtksignal.h>
1447 #include <gtk/gtkmenuitem.h>
1448 #include <gtk/gtkmenu.h>
1449 #include <gtk/gtkcontainer.h>
1454 static void gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass
*klass
);
1455 static void gtk_pixmap_menu_item_init (GtkPixmapMenuItem
*menu_item
);
1456 static void gtk_pixmap_menu_item_draw (GtkWidget
*widget
,
1457 GdkRectangle
*area
);
1458 static gint
gtk_pixmap_menu_item_expose (GtkWidget
*widget
,
1459 GdkEventExpose
*event
);
1461 /* we must override the following functions */
1463 static void gtk_pixmap_menu_item_map (GtkWidget
*widget
);
1464 static void gtk_pixmap_menu_item_size_allocate (GtkWidget
*widget
,
1465 GtkAllocation
*allocation
);
1466 static void gtk_pixmap_menu_item_forall (GtkContainer
*container
,
1467 gboolean include_internals
,
1468 GtkCallback callback
,
1469 gpointer callback_data
);
1470 static void gtk_pixmap_menu_item_size_request (GtkWidget
*widget
,
1471 GtkRequisition
*requisition
);
1472 static void gtk_pixmap_menu_item_remove (GtkContainer
*container
,
1475 static void changed_have_pixmap_status (GtkPixmapMenuItem
*menu_item
);
1477 static GtkMenuItemClass
*parent_class
= NULL
;
1481 #define BORDER_SPACING 3
1482 #define PMAP_WIDTH 20
1485 gtk_pixmap_menu_item_get_type (void)
1487 static GtkType pixmap_menu_item_type
= 0;
1489 if (!pixmap_menu_item_type
)
1491 GtkTypeInfo pixmap_menu_item_info
=
1493 (char *)"GtkPixmapMenuItem",
1494 sizeof (GtkPixmapMenuItem
),
1495 sizeof (GtkPixmapMenuItemClass
),
1496 (GtkClassInitFunc
) gtk_pixmap_menu_item_class_init
,
1497 (GtkObjectInitFunc
) gtk_pixmap_menu_item_init
,
1498 /* reserved_1 */ NULL
,
1499 /* reserved_2 */ NULL
,
1500 (GtkClassInitFunc
) NULL
,
1503 pixmap_menu_item_type
= gtk_type_unique (gtk_menu_item_get_type (),
1504 &pixmap_menu_item_info
);
1507 return pixmap_menu_item_type
;
1511 * gtk_pixmap_menu_item_new
1513 * Creates a new pixmap menu item. Use gtk_pixmap_menu_item_set_pixmap()
1514 * to set the pixmap wich is displayed at the left side.
1517 * &GtkWidget pointer to new menu item
1521 gtk_pixmap_menu_item_new (void)
1523 return GTK_WIDGET (gtk_type_new (gtk_pixmap_menu_item_get_type ()));
1527 gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass
*klass
)
1529 GtkObjectClass
*object_class
;
1530 GtkWidgetClass
*widget_class
;
1531 GtkMenuItemClass
*menu_item_class
;
1532 GtkContainerClass
*container_class
;
1534 object_class
= (GtkObjectClass
*) klass
;
1535 widget_class
= (GtkWidgetClass
*) klass
;
1536 menu_item_class
= (GtkMenuItemClass
*) klass
;
1537 container_class
= (GtkContainerClass
*) klass
;
1539 parent_class
= (GtkMenuItemClass
*) gtk_type_class (gtk_menu_item_get_type ());
1541 widget_class
->draw
= gtk_pixmap_menu_item_draw
;
1542 widget_class
->expose_event
= gtk_pixmap_menu_item_expose
;
1543 widget_class
->map
= gtk_pixmap_menu_item_map
;
1544 widget_class
->size_allocate
= gtk_pixmap_menu_item_size_allocate
;
1545 widget_class
->size_request
= gtk_pixmap_menu_item_size_request
;
1547 container_class
->forall
= gtk_pixmap_menu_item_forall
;
1548 container_class
->remove
= gtk_pixmap_menu_item_remove
;
1550 klass
->orig_toggle_size
= menu_item_class
->toggle_size
;
1551 klass
->have_pixmap_count
= 0;
1555 gtk_pixmap_menu_item_init (GtkPixmapMenuItem
*menu_item
)
1559 mi
= GTK_MENU_ITEM (menu_item
);
1561 menu_item
->pixmap
= NULL
;
1565 gtk_pixmap_menu_item_draw (GtkWidget
*widget
,
1568 g_return_if_fail (widget
!= NULL
);
1569 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
));
1570 g_return_if_fail (area
!= NULL
);
1572 if (GTK_WIDGET_CLASS (parent_class
)->draw
)
1573 (* GTK_WIDGET_CLASS (parent_class
)->draw
) (widget
, area
);
1575 if (GTK_WIDGET_DRAWABLE (widget
) &&
1576 GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
) {
1577 gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
),NULL
);
1582 gtk_pixmap_menu_item_expose (GtkWidget
*widget
,
1583 GdkEventExpose
*event
)
1585 g_return_val_if_fail (widget
!= NULL
, FALSE
);
1586 g_return_val_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
), FALSE
);
1587 g_return_val_if_fail (event
!= NULL
, FALSE
);
1589 if (GTK_WIDGET_CLASS (parent_class
)->expose_event
)
1590 (* GTK_WIDGET_CLASS (parent_class
)->expose_event
) (widget
, event
);
1592 if (GTK_WIDGET_DRAWABLE (widget
) &&
1593 GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
) {
1594 gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
),NULL
);
1601 * gtk_pixmap_menu_item_set_pixmap
1602 * @menu_item: Pointer to the pixmap menu item
1603 * @pixmap: Pointer to a pixmap widget
1605 * Set the pixmap of the menu item.
1610 gtk_pixmap_menu_item_set_pixmap (GtkPixmapMenuItem
*menu_item
,
1613 g_return_if_fail (menu_item
!= NULL
);
1614 g_return_if_fail (pixmap
!= NULL
);
1615 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (menu_item
));
1616 g_return_if_fail (GTK_IS_WIDGET (pixmap
));
1617 g_return_if_fail (menu_item
->pixmap
== NULL
);
1619 gtk_widget_set_parent (pixmap
, GTK_WIDGET (menu_item
));
1620 menu_item
->pixmap
= pixmap
;
1622 if (GTK_WIDGET_REALIZED (pixmap
->parent
) &&
1623 !GTK_WIDGET_REALIZED (pixmap
))
1624 gtk_widget_realize (pixmap
);
1626 if (GTK_WIDGET_VISIBLE (pixmap
->parent
)) {
1627 if (GTK_WIDGET_MAPPED (pixmap
->parent
) &&
1628 GTK_WIDGET_VISIBLE(pixmap
) &&
1629 !GTK_WIDGET_MAPPED (pixmap
))
1630 gtk_widget_map (pixmap
);
1633 changed_have_pixmap_status(menu_item
);
1635 if (GTK_WIDGET_VISIBLE (pixmap
) && GTK_WIDGET_VISIBLE (menu_item
))
1636 gtk_widget_queue_resize (pixmap
);
1640 gtk_pixmap_menu_item_map (GtkWidget
*widget
)
1642 GtkPixmapMenuItem
*menu_item
;
1644 g_return_if_fail (widget
!= NULL
);
1645 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
));
1647 menu_item
= GTK_PIXMAP_MENU_ITEM(widget
);
1649 GTK_WIDGET_CLASS(parent_class
)->map(widget
);
1651 if (menu_item
->pixmap
&&
1652 GTK_WIDGET_VISIBLE (menu_item
->pixmap
) &&
1653 !GTK_WIDGET_MAPPED (menu_item
->pixmap
))
1654 gtk_widget_map (menu_item
->pixmap
);
1658 gtk_pixmap_menu_item_size_allocate (GtkWidget
*widget
,
1659 GtkAllocation
*allocation
)
1661 GtkPixmapMenuItem
*pmenu_item
;
1663 pmenu_item
= GTK_PIXMAP_MENU_ITEM(widget
);
1665 if (pmenu_item
->pixmap
&& GTK_WIDGET_VISIBLE(pmenu_item
))
1667 GtkAllocation child_allocation
;
1670 border_width
= GTK_CONTAINER (widget
)->border_width
;
1672 child_allocation
.width
= pmenu_item
->pixmap
->requisition
.width
;
1673 child_allocation
.height
= pmenu_item
->pixmap
->requisition
.height
;
1674 child_allocation
.x
= border_width
+ BORDER_SPACING
;
1675 child_allocation
.y
= (border_width
+ BORDER_SPACING
1676 + (((allocation
->height
- child_allocation
.height
) - child_allocation
.x
)
1677 / 2)); /* center pixmaps vertically */
1678 gtk_widget_size_allocate (pmenu_item
->pixmap
, &child_allocation
);
1681 if (GTK_WIDGET_CLASS (parent_class
)->size_allocate
)
1682 GTK_WIDGET_CLASS(parent_class
)->size_allocate (widget
, allocation
);
1686 gtk_pixmap_menu_item_forall (GtkContainer
*container
,
1687 gboolean include_internals
,
1688 GtkCallback callback
,
1689 gpointer callback_data
)
1691 GtkPixmapMenuItem
*menu_item
;
1693 g_return_if_fail (container
!= NULL
);
1694 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container
));
1695 g_return_if_fail (callback
!= NULL
);
1697 menu_item
= GTK_PIXMAP_MENU_ITEM (container
);
1699 if (menu_item
->pixmap
)
1700 (* callback
) (menu_item
->pixmap
, callback_data
);
1702 GTK_CONTAINER_CLASS(parent_class
)->forall(container
,include_internals
,
1703 callback
,callback_data
);
1707 gtk_pixmap_menu_item_size_request (GtkWidget
*widget
,
1708 GtkRequisition
*requisition
)
1710 GtkPixmapMenuItem
*menu_item
;
1711 GtkRequisition req
= {0, 0};
1713 g_return_if_fail (widget
!= NULL
);
1714 g_return_if_fail (GTK_IS_MENU_ITEM (widget
));
1715 g_return_if_fail (requisition
!= NULL
);
1717 GTK_WIDGET_CLASS(parent_class
)->size_request(widget
,requisition
);
1719 menu_item
= GTK_PIXMAP_MENU_ITEM (widget
);
1721 if (menu_item
->pixmap
)
1722 gtk_widget_size_request(menu_item
->pixmap
, &req
);
1724 requisition
->height
= MAX(req
.height
+ GTK_CONTAINER(widget
)->border_width
+ BORDER_SPACING
, (unsigned int) requisition
->height
);
1725 requisition
->width
+= (req
.width
+ GTK_CONTAINER(widget
)->border_width
+ BORDER_SPACING
);
1729 gtk_pixmap_menu_item_remove (GtkContainer
*container
,
1733 gboolean widget_was_visible
;
1735 g_return_if_fail (container
!= NULL
);
1736 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container
));
1737 g_return_if_fail (child
!= NULL
);
1738 g_return_if_fail (GTK_IS_WIDGET (child
));
1740 bin
= GTK_BIN (container
);
1741 g_return_if_fail ((bin
->child
== child
||
1742 (GTK_PIXMAP_MENU_ITEM(container
)->pixmap
== child
)));
1744 widget_was_visible
= GTK_WIDGET_VISIBLE (child
);
1746 gtk_widget_unparent (child
);
1747 if (bin
->child
== child
)
1750 GTK_PIXMAP_MENU_ITEM(container
)->pixmap
= NULL
;
1751 changed_have_pixmap_status(GTK_PIXMAP_MENU_ITEM(container
));
1754 if (widget_was_visible
)
1755 gtk_widget_queue_resize (GTK_WIDGET (container
));
1759 /* important to only call this if there was actually a _change_ in pixmap == NULL */
1761 changed_have_pixmap_status (GtkPixmapMenuItem
*menu_item
)
1763 if (menu_item
->pixmap
!= NULL
) {
1764 GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
+= 1;
1766 if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
== 1) {
1767 /* Install pixmap toggle size */
1768 GTK_MENU_ITEM_GET_CLASS(menu_item
)->toggle_size
= MAX(GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->orig_toggle_size
, PMAP_WIDTH
);
1771 GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
-= 1;
1773 if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
== 0) {
1774 /* Install normal toggle size */
1775 GTK_MENU_ITEM_GET_CLASS(menu_item
)->toggle_size
= GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->orig_toggle_size
;
1779 /* Note that we actually need to do this for _all_ GtkPixmapMenuItem
1780 whenever the klass->toggle_size changes; but by doing it anytime
1781 this function is called, we get the same effect, just because of
1782 how the preferences option to show pixmaps works. Bogus, broken.
1784 if (GTK_WIDGET_VISIBLE(GTK_WIDGET(menu_item
)))
1785 gtk_widget_queue_resize(GTK_WIDGET(menu_item
));
1788 #endif // USE_MENU_BITMAPS