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 wxEvtHandler
* handler
= menu
->GetEventHandler();
176 if (handler
&& handler
->ProcessEvent(event
))
179 wxWindow
*win
= menu
->GetInvokingWindow();
180 if (win
) win
->GetEventHandler()->ProcessEvent( event
);
183 //-----------------------------------------------------------------------------
185 //-----------------------------------------------------------------------------
187 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar
,wxWindow
)
189 wxMenuBar::wxMenuBar( long style
)
191 /* the parent window is known after wxFrame::SetMenu() */
192 m_needParent
= FALSE
;
194 m_invokingWindow
= (wxWindow
*) NULL
;
196 if (!PreCreation( (wxWindow
*) NULL
, wxDefaultPosition
, wxDefaultSize
) ||
197 !CreateBase( (wxWindow
*) NULL
, -1, wxDefaultPosition
, wxDefaultSize
, style
, wxDefaultValidator
, wxT("menubar") ))
199 wxFAIL_MSG( wxT("wxMenuBar creation failed") );
203 m_menus
.DeleteContents( TRUE
);
205 /* GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has. */
206 #if GTK_CHECK_VERSION(1, 2, 1)
207 m_accel
= gtk_accel_group_new();
208 m_factory
= gtk_item_factory_new( GTK_TYPE_MENU_BAR
, "<main>", m_accel
);
209 m_menubar
= gtk_item_factory_get_widget( m_factory
, "<main>" );
211 m_menubar
= gtk_menu_bar_new();
214 if (style
& wxMB_DOCKABLE
)
216 m_widget
= gtk_handle_box_new();
217 gtk_container_add( GTK_CONTAINER(m_widget
), GTK_WIDGET(m_menubar
) );
218 gtk_widget_show( GTK_WIDGET(m_menubar
) );
222 m_widget
= GTK_WIDGET(m_menubar
);
230 wxMenuBar::wxMenuBar()
232 /* the parent window is known after wxFrame::SetMenu() */
233 m_needParent
= FALSE
;
235 m_invokingWindow
= (wxWindow
*) NULL
;
237 if (!PreCreation( (wxWindow
*) NULL
, wxDefaultPosition
, wxDefaultSize
) ||
238 !CreateBase( (wxWindow
*) NULL
, -1, wxDefaultPosition
, wxDefaultSize
, 0, wxDefaultValidator
, wxT("menubar") ))
240 wxFAIL_MSG( wxT("wxMenuBar creation failed") );
244 m_menus
.DeleteContents( TRUE
);
246 /* GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has. */
247 #if GTK_CHECK_VERSION(1, 2, 1)
248 m_accel
= gtk_accel_group_new();
249 m_factory
= gtk_item_factory_new( GTK_TYPE_MENU_BAR
, "<main>", m_accel
);
250 m_menubar
= gtk_item_factory_get_widget( m_factory
, "<main>" );
252 m_menubar
= gtk_menu_bar_new();
255 m_widget
= GTK_WIDGET(m_menubar
);
262 wxMenuBar::~wxMenuBar()
264 // gtk_object_unref( GTK_OBJECT(m_factory) ); why not ?
267 static void wxMenubarUnsetInvokingWindow( wxMenu
*menu
, wxWindow
*win
)
269 menu
->SetInvokingWindow( (wxWindow
*) NULL
);
271 wxWindow
*top_frame
= win
;
272 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
273 top_frame
= top_frame
->GetParent();
275 /* support for native hot keys */
276 gtk_accel_group_detach( menu
->m_accel
, ACCEL_OBJ_CAST(top_frame
->m_widget
) );
278 wxMenuItemList::Node
*node
= menu
->GetMenuItems().GetFirst();
281 wxMenuItem
*menuitem
= node
->GetData();
282 if (menuitem
->IsSubMenu())
283 wxMenubarUnsetInvokingWindow( menuitem
->GetSubMenu(), win
);
284 node
= node
->GetNext();
288 static void wxMenubarSetInvokingWindow( wxMenu
*menu
, wxWindow
*win
)
290 menu
->SetInvokingWindow( win
);
292 #if GTK_CHECK_VERSION(1, 2, 1)
293 wxWindow
*top_frame
= win
;
294 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
295 top_frame
= top_frame
->GetParent();
297 /* support for native hot keys */
298 ACCEL_OBJECT
*obj
= ACCEL_OBJ_CAST(top_frame
->m_widget
);
299 if ( !g_slist_find( ACCEL_OBJECTS(menu
->m_accel
), obj
) )
300 gtk_accel_group_attach( menu
->m_accel
, obj
);
301 #endif // GTK+ 1.2.1+
303 wxMenuItemList::Node
*node
= menu
->GetMenuItems().GetFirst();
306 wxMenuItem
*menuitem
= node
->GetData();
307 if (menuitem
->IsSubMenu())
308 wxMenubarSetInvokingWindow( menuitem
->GetSubMenu(), win
);
309 node
= node
->GetNext();
313 void wxMenuBar::SetInvokingWindow( wxWindow
*win
)
315 m_invokingWindow
= win
;
316 #if GTK_CHECK_VERSION(1, 2, 1)
317 wxWindow
*top_frame
= win
;
318 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
319 top_frame
= top_frame
->GetParent();
321 /* support for native key accelerators indicated by underscroes */
322 ACCEL_OBJECT
*obj
= ACCEL_OBJ_CAST(top_frame
->m_widget
);
323 if ( !g_slist_find( ACCEL_OBJECTS(m_accel
), obj
) )
324 gtk_accel_group_attach( m_accel
, obj
);
325 #endif // GTK+ 1.2.1+
327 wxMenuList::Node
*node
= m_menus
.GetFirst();
330 wxMenu
*menu
= node
->GetData();
331 wxMenubarSetInvokingWindow( menu
, win
);
332 node
= node
->GetNext();
336 void wxMenuBar::UnsetInvokingWindow( wxWindow
*win
)
338 m_invokingWindow
= (wxWindow
*) NULL
;
339 #if GTK_CHECK_VERSION(1, 2, 1)
340 wxWindow
*top_frame
= win
;
341 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
342 top_frame
= top_frame
->GetParent();
344 // support for native key accelerators indicated by underscroes
345 gtk_accel_group_detach( m_accel
, ACCEL_OBJ_CAST(top_frame
->m_widget
) );
346 #endif // GTK+ 1.2.1+
348 wxMenuList::Node
*node
= m_menus
.GetFirst();
351 wxMenu
*menu
= node
->GetData();
352 wxMenubarUnsetInvokingWindow( menu
, win
);
353 node
= node
->GetNext();
357 bool wxMenuBar::Append( wxMenu
*menu
, const wxString
&title
)
359 if ( !wxMenuBarBase::Append( menu
, title
) )
362 return GtkAppend(menu
, title
);
365 bool wxMenuBar::GtkAppend(wxMenu
*menu
, const wxString
& title
)
367 wxString
str( wxReplaceUnderscore( title
) );
369 // This doesn't have much effect right now.
370 menu
->SetTitle( str
);
372 // GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has.
373 #if GTK_CHECK_VERSION(1, 2, 1)
376 buf
<< wxT('/') << str
.c_str();
378 // local buffer in multibyte form
380 strcpy(cbuf
, wxGTK_CONV(buf
) );
382 GtkItemFactoryEntry entry
;
383 entry
.path
= (gchar
*)cbuf
; // const_cast
384 entry
.accelerator
= (gchar
*) NULL
;
385 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
386 entry
.callback_action
= 0;
387 entry
.item_type
= (char *)"<Branch>";
389 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
390 // in order to get the pointer to the item we need the item text _without_ underscores
391 wxString tmp
= wxT("<main>/");
393 for ( pc
= str
; *pc
!= wxT('\0'); pc
++ )
395 // contrary to the common sense, we must throw out _all_ underscores,
396 // (i.e. "Hello__World" => "HelloWorld" and not "Hello_World" as we
397 // might naively think). IMHO it's a bug in GTK+ (VZ)
398 while (*pc
== wxT('_'))
402 menu
->m_owner
= gtk_item_factory_get_item( m_factory
, wxGTK_CONV( tmp
) );
403 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu
->m_owner
), menu
->m_menu
);
406 menu
->m_owner
= gtk_menu_item_new_with_label( wxGTK_CONV( str
) );
407 gtk_widget_show( menu
->m_owner
);
408 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu
->m_owner
), menu
->m_menu
);
410 gtk_menu_bar_append( GTK_MENU_BAR(m_menubar
), menu
->m_owner
);
414 gtk_signal_connect( GTK_OBJECT(menu
->m_owner
), "activate",
415 GTK_SIGNAL_FUNC(gtk_menu_open_callback
),
418 // m_invokingWindow is set after wxFrame::SetMenuBar(). This call enables
419 // addings menu later on.
420 if (m_invokingWindow
)
422 wxMenubarSetInvokingWindow( menu
, m_invokingWindow
);
424 // OPTIMISE ME: we should probably cache this, or pass it
425 // directly, but for now this is a minimal
426 // change to validate the new dynamic sizing.
427 // see (and refactor :) similar code in Remove
430 wxFrame
*frame
= wxDynamicCast( m_invokingWindow
, wxFrame
);
433 frame
->UpdateMenuBarSize();
439 bool wxMenuBar::Insert(size_t pos
, wxMenu
*menu
, const wxString
& title
)
441 if ( !wxMenuBarBase::Insert(pos
, menu
, title
) )
444 // GTK+ doesn't have a function to insert a menu using GtkItemFactory (as
445 // of version 1.2.6), so we first append the item and then change its
447 if ( !GtkAppend(menu
, title
) )
450 if (pos
+1 >= m_menus
.GetCount())
453 GtkMenuShell
*menu_shell
= GTK_MENU_SHELL(m_factory
->widget
);
454 gpointer data
= g_list_last(menu_shell
->children
)->data
;
455 menu_shell
->children
= g_list_remove(menu_shell
->children
, data
);
456 menu_shell
->children
= g_list_insert(menu_shell
->children
, data
, pos
);
461 wxMenu
*wxMenuBar::Replace(size_t pos
, wxMenu
*menu
, const wxString
& title
)
463 // remove the old item and insert a new one
464 wxMenu
*menuOld
= Remove(pos
);
465 if ( menuOld
&& !Insert(pos
, menu
, title
) )
467 return (wxMenu
*) NULL
;
470 // either Insert() succeeded or Remove() failed and menuOld is NULL
474 static wxMenu
*CopyMenu (wxMenu
*menu
)
476 wxMenu
*menucopy
= new wxMenu ();
477 wxMenuItemList::Node
*node
= menu
->GetMenuItems().GetFirst();
480 wxMenuItem
*item
= node
->GetData();
481 int itemid
= item
->GetId();
482 wxString text
= item
->GetText();
483 text
.Replace(wxT("_"), wxT("&"));
484 wxMenu
*submenu
= item
->GetSubMenu();
487 wxMenuItem
* itemcopy
= new wxMenuItem(menucopy
,
489 menu
->GetHelpString(itemid
));
490 itemcopy
->SetBitmap(item
->GetBitmap());
491 itemcopy
->SetCheckable(item
->IsCheckable());
492 menucopy
->Append(itemcopy
);
495 menucopy
->Append (itemid
, text
, CopyMenu(submenu
),
496 menu
->GetHelpString(itemid
));
498 node
= node
->GetNext();
504 wxMenu
*wxMenuBar::Remove(size_t pos
)
506 wxMenu
*menu
= wxMenuBarBase::Remove(pos
);
508 return (wxMenu
*) NULL
;
511 GtkMenuShell *menu_shell = GTK_MENU_SHELL(m_factory->widget);
513 printf( "factory entries before %d\n", (int)g_slist_length(m_factory->items) );
514 printf( "menu shell entries before %d\n", (int)g_list_length( menu_shell->children ) );
517 wxMenu
*menucopy
= CopyMenu( menu
);
519 // unparent calls unref() and that would delete the widget so we raise
520 // the ref count to 2 artificially before invoking unparent.
521 gtk_widget_ref( menu
->m_menu
);
522 gtk_widget_unparent( menu
->m_menu
);
524 gtk_widget_destroy( menu
->m_owner
);
529 printf( "factory entries after %d\n", (int)g_slist_length(m_factory->items) );
530 printf( "menu shell entries after %d\n", (int)g_list_length( menu_shell->children ) );
533 if (m_invokingWindow
)
535 // OPTIMISE ME: see comment in GtkAppend
537 wxFrame
*frame
= wxDynamicCast( m_invokingWindow
, wxFrame
);
540 frame
->UpdateMenuBarSize();
546 static int FindMenuItemRecursive( const wxMenu
*menu
, const wxString
&menuString
, const wxString
&itemString
)
548 if (wxMenuItem::GetLabelFromText(menu
->GetTitle()) == wxMenuItem::GetLabelFromText(menuString
))
550 int res
= menu
->FindItem( itemString
);
551 if (res
!= wxNOT_FOUND
)
555 wxMenuItemList::Node
*node
= menu
->GetMenuItems().GetFirst();
558 wxMenuItem
*item
= node
->GetData();
559 if (item
->IsSubMenu())
560 return FindMenuItemRecursive(item
->GetSubMenu(), menuString
, itemString
);
562 node
= node
->GetNext();
568 int wxMenuBar::FindMenuItem( const wxString
&menuString
, const wxString
&itemString
) const
570 wxMenuList::Node
*node
= m_menus
.GetFirst();
573 wxMenu
*menu
= node
->GetData();
574 int res
= FindMenuItemRecursive( menu
, menuString
, itemString
);
577 node
= node
->GetNext();
583 // Find a wxMenuItem using its id. Recurses down into sub-menus
584 static wxMenuItem
* FindMenuItemByIdRecursive(const wxMenu
* menu
, int id
)
586 wxMenuItem
* result
= menu
->FindChildItem(id
);
588 wxMenuItemList::Node
*node
= menu
->GetMenuItems().GetFirst();
589 while ( node
&& result
== NULL
)
591 wxMenuItem
*item
= node
->GetData();
592 if (item
->IsSubMenu())
594 result
= FindMenuItemByIdRecursive( item
->GetSubMenu(), id
);
596 node
= node
->GetNext();
602 wxMenuItem
* wxMenuBar::FindItem( int id
, wxMenu
**menuForItem
) const
604 wxMenuItem
* result
= 0;
605 wxMenuList::Node
*node
= m_menus
.GetFirst();
606 while (node
&& result
== 0)
608 wxMenu
*menu
= node
->GetData();
609 result
= FindMenuItemByIdRecursive( menu
, id
);
610 node
= node
->GetNext();
615 *menuForItem
= result
? result
->GetMenu() : (wxMenu
*)NULL
;
621 void wxMenuBar::EnableTop( size_t pos
, bool flag
)
623 wxMenuList::Node
*node
= m_menus
.Item( pos
);
625 wxCHECK_RET( node
, wxT("menu not found") );
627 wxMenu
* menu
= node
->GetData();
630 gtk_widget_set_sensitive( menu
->m_owner
, flag
);
633 wxString
wxMenuBar::GetLabelTop( size_t pos
) const
635 wxMenuList::Node
*node
= m_menus
.Item( pos
);
637 wxCHECK_MSG( node
, wxT("invalid"), wxT("menu not found") );
639 wxMenu
* menu
= node
->GetData();
642 wxString
text( menu
->GetTitle() );
643 for ( const wxChar
*pc
= text
.c_str(); *pc
; pc
++ )
645 if ( *pc
== wxT('_') )
647 // '_' is the escape character for GTK+
651 // don't remove ampersands '&' since if we have them in the menu title
652 // it means that they were doubled to indicate "&" instead of accelerator
660 void wxMenuBar::SetLabelTop( size_t pos
, const wxString
& label
)
662 wxMenuList::Node
*node
= m_menus
.Item( pos
);
664 wxCHECK_RET( node
, wxT("menu not found") );
666 wxMenu
* menu
= node
->GetData();
668 wxString
str( wxReplaceUnderscore( label
) );
670 menu
->SetTitle( str
);
674 GtkLabel
*label
= GTK_LABEL( GTK_BIN(menu
->m_owner
)->child
);
677 gtk_label_set( label
, wxGTK_CONV( str
) );
679 /* reparse key accel */
680 (void)gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV( str
) );
681 gtk_accel_label_refetch( GTK_ACCEL_LABEL(label
) );
686 //-----------------------------------------------------------------------------
688 //-----------------------------------------------------------------------------
690 static void gtk_menu_clicked_callback( GtkWidget
*widget
, wxMenu
*menu
)
693 wxapp_install_idle_handler();
695 int id
= menu
->FindMenuIdByMenuItem(widget
);
697 /* should find it for normal (not popup) menu */
698 wxASSERT_MSG( (id
!= -1) || (menu
->GetInvokingWindow() != NULL
),
699 _T("menu item not found in gtk_menu_clicked_callback") );
701 if (!menu
->IsEnabled(id
))
704 if ( menu
->IsAttached() ) // is this menu on a menubar?
706 wxFrame
* frame
= menu
->GetMenuBar()->GetFrame();
707 frame
->ProcessCommand(id
);
711 wxMenuItem
* item
= menu
->FindChildItem( id
);
712 wxCHECK_RET( item
, wxT("error in menu item callback") );
714 if (item
->IsCheckable())
716 bool isReallyChecked
= item
->IsChecked(),
717 isInternallyChecked
= item
->wxMenuItemBase::IsChecked();
719 // ensure that the internal state is always consistent with what is
720 // shown on the screen
721 item
->wxMenuItemBase::Check(isReallyChecked
);
723 // we must not report the events for the radio button going up nor the
724 // events resulting from the calls to wxMenuItem::Check()
725 if ( (item
->GetKind() == wxITEM_RADIO
&& !isReallyChecked
) ||
726 (isInternallyChecked
== isReallyChecked
) )
731 // the user pressed on the menu item: report the event below
734 menu
->SendEvent(id
, item
->IsCheckable() ? item
->IsChecked() : -1);
738 //-----------------------------------------------------------------------------
740 //-----------------------------------------------------------------------------
742 static void gtk_menu_hilight_callback( GtkWidget
*widget
, wxMenu
*menu
)
744 if (g_isIdle
) wxapp_install_idle_handler();
746 int id
= menu
->FindMenuIdByMenuItem(widget
);
748 wxASSERT( id
!= -1 ); // should find it!
750 if (!menu
->IsEnabled(id
))
753 wxMenuEvent
event( wxEVT_MENU_HIGHLIGHT
, id
);
754 event
.SetEventObject( menu
);
756 wxEvtHandler
* handler
= menu
->GetEventHandler();
757 if (handler
&& handler
->ProcessEvent(event
))
760 wxWindow
*win
= menu
->GetInvokingWindow();
761 if (win
) win
->GetEventHandler()->ProcessEvent( event
);
764 //-----------------------------------------------------------------------------
766 //-----------------------------------------------------------------------------
768 static void gtk_menu_nolight_callback( GtkWidget
*widget
, wxMenu
*menu
)
770 if (g_isIdle
) wxapp_install_idle_handler();
772 int id
= menu
->FindMenuIdByMenuItem(widget
);
774 wxASSERT( id
!= -1 ); // should find it!
776 if (!menu
->IsEnabled(id
))
779 wxMenuEvent
event( wxEVT_MENU_HIGHLIGHT
, -1 );
780 event
.SetEventObject( menu
);
782 wxEvtHandler
* handler
= menu
->GetEventHandler();
783 if (handler
&& handler
->ProcessEvent(event
))
786 wxWindow
*win
= menu
->GetInvokingWindow();
788 win
->GetEventHandler()->ProcessEvent( event
);
791 //-----------------------------------------------------------------------------
793 //-----------------------------------------------------------------------------
795 IMPLEMENT_DYNAMIC_CLASS(wxMenuItem
, wxObject
)
797 wxMenuItem
*wxMenuItemBase::New(wxMenu
*parentMenu
,
799 const wxString
& name
,
800 const wxString
& help
,
804 return new wxMenuItem(parentMenu
, id
, name
, help
, kind
, subMenu
);
807 wxMenuItem::wxMenuItem(wxMenu
*parentMenu
,
809 const wxString
& text
,
810 const wxString
& help
,
813 : wxMenuItemBase(parentMenu
, id
, text
, help
, kind
, subMenu
)
818 wxMenuItem::wxMenuItem(wxMenu
*parentMenu
,
820 const wxString
& text
,
821 const wxString
& help
,
824 : wxMenuItemBase(parentMenu
, id
, text
, help
,
825 isCheckable
? wxITEM_CHECK
: wxITEM_NORMAL
, subMenu
)
830 void wxMenuItem::Init(const wxString
& text
)
832 m_labelWidget
= (GtkWidget
*) NULL
;
833 m_menuItem
= (GtkWidget
*) NULL
;
838 wxMenuItem::~wxMenuItem()
840 // don't delete menu items, the menus take care of that
843 // return the menu item text without any menu accels
845 wxString
wxMenuItemBase::GetLabelFromText(const wxString
& text
)
849 for ( const wxChar
*pc
= text
.c_str(); *pc
; pc
++ )
851 if ( *pc
== wxT('_') )
853 // GTK 1.2 escapes "xxx_xxx" to "xxx__xxx"
859 #if GTK_CHECK_VERSION(2, 0, 0)
860 if ( *pc
== wxT('\\') )
862 // GTK 2.0 escapes "xxx/xxx" to "xxx\/xxx"
869 if ( (*pc
== wxT('&')) && (*(pc
+1) != wxT('&')) )
872 // "&" is doubled to indicate "&" instead of accelerator
879 // wxPrintf( L"text %s label %s\n", text.c_str(), label.c_str() );
884 void wxMenuItem::SetText( const wxString
& str
)
886 // Some optimization to avoid flicker
887 wxString oldLabel
= m_text
;
888 oldLabel
= wxStripMenuCodes(oldLabel
.BeforeFirst('\t'));
889 oldLabel
.Replace(wxT("_"), wxT(""));
890 wxString label1
= wxStripMenuCodes(str
.BeforeFirst('\t'));
891 if (oldLabel
== label1
)
900 label
= (GtkLabel
*) m_labelWidget
;
902 label
= GTK_LABEL( GTK_BIN(m_menuItem
)->child
);
904 #if GTK_CHECK_VERSION(2, 0, 0)
905 // We have to imitate item_factory_unescape_label here
907 for (size_t n
= 0; n
< m_text
.Len(); n
++)
909 if (m_text
[n
] != wxT('\\'))
913 gtk_label_set_text_with_mnemonic( GTK_LABEL(label
), wxGTK_CONV(tmp
) );
916 gtk_label_set( label
, wxGTK_CONV( m_text
) );
919 (void)gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV(m_text
) );
920 gtk_accel_label_refetch( GTK_ACCEL_LABEL(label
) );
925 // it's valid for this function to be called even if m_menuItem == NULL
926 void wxMenuItem::DoSetText( const wxString
& str
)
928 // '\t' is the deliminator indicating a hot key
930 const wxChar
*pc
= str
;
931 while ( (*pc
!= wxT('\0')) && (*pc
!= wxT('\t')) )
933 if ((*pc
== wxT('&')) && (*(pc
+1) == wxT('&')))
935 // "&" is doubled to indicate "&" instead of accelerator
939 else if (*pc
== wxT('&'))
943 #if GTK_CHECK_VERSION(2, 0, 0)
944 else if ( *pc
== wxT('_') ) // escape underscores
946 // m_text << wxT("__"); doesn't work
949 else if (*pc
== wxT('/')) // we have to escape slashes
951 m_text
<< wxT("\\/");
953 else if (*pc
== wxT('\\')) // we have to double backslashes
955 m_text
<< wxT("\\\\");
958 else if ( *pc
== wxT('_') ) // escape underscores
962 else if (*pc
== wxT('/')) /* we have to filter out slashes ... */
964 m_text
<< wxT('\\'); /* ... and replace them with back slashes */
973 // wxPrintf( L"str %s m_text %s\n", str.c_str(), m_text.c_str() );
986 wxAcceleratorEntry
*wxMenuItem::GetAccel() const
991 return (wxAcceleratorEntry
*)NULL
;
994 // as wxGetAccelFromString() looks for TAB, insert a dummy one here
996 label
<< wxT('\t') << GetHotKey();
998 return wxGetAccelFromString(label
);
1001 #endif // wxUSE_ACCEL
1003 void wxMenuItem::Check( bool check
)
1005 wxCHECK_RET( m_menuItem
, wxT("invalid menu item") );
1007 if (check
== m_isChecked
)
1010 wxMenuItemBase::Check( check
);
1012 switch ( GetKind() )
1016 gtk_check_menu_item_set_state( (GtkCheckMenuItem
*)m_menuItem
, (gint
)check
);
1020 wxFAIL_MSG( _T("can't check this item") );
1024 void wxMenuItem::Enable( bool enable
)
1026 wxCHECK_RET( m_menuItem
, wxT("invalid menu item") );
1028 gtk_widget_set_sensitive( m_menuItem
, enable
);
1029 wxMenuItemBase::Enable( enable
);
1032 bool wxMenuItem::IsChecked() const
1034 wxCHECK_MSG( m_menuItem
, FALSE
, wxT("invalid menu item") );
1036 wxCHECK_MSG( IsCheckable(), FALSE
,
1037 wxT("can't get state of uncheckable item!") );
1039 return ((GtkCheckMenuItem
*)m_menuItem
)->active
!= 0;
1042 wxString
wxMenuItem::GetFactoryPath() const
1044 // In order to get the pointer to the item we need the item
1045 // text _without_ underscores in GTK 1.2
1046 wxString
path( wxT("<main>/") );
1048 for ( const wxChar
*pc
= m_text
.c_str(); *pc
; pc
++ )
1050 if ( *pc
== wxT('_') )
1055 // remove '_' unconditionally
1060 // don't remove ampersands '&' since if we have them in the menu item title
1061 // it means that they were doubled to indicate "&" instead of accelerator
1069 //-----------------------------------------------------------------------------
1071 //-----------------------------------------------------------------------------
1073 IMPLEMENT_DYNAMIC_CLASS(wxMenu
,wxEvtHandler
)
1077 m_accel
= gtk_accel_group_new();
1078 m_factory
= gtk_item_factory_new( GTK_TYPE_MENU
, "<main>", m_accel
);
1079 m_menu
= gtk_item_factory_get_widget( m_factory
, "<main>" );
1081 m_owner
= (GtkWidget
*) NULL
;
1083 // Tearoffs are entries, just like separators. So if we want this
1084 // menu to be a tear-off one, we just append a tearoff entry
1086 if(m_style
& wxMENU_TEAROFF
)
1088 GtkItemFactoryEntry entry
;
1089 entry
.path
= (char *)"/tearoff";
1090 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
1091 entry
.callback_action
= 0;
1092 entry
.item_type
= (char *)"<Tearoff>";
1093 entry
.accelerator
= (gchar
*) NULL
;
1094 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
1095 //GtkWidget *menuItem = gtk_item_factory_get_widget( m_factory, "<main>/tearoff" );
1098 // append the title as the very first entry if we have it
1101 Append(-2, m_title
);
1110 gtk_widget_destroy( m_menu
);
1112 gtk_object_unref( GTK_OBJECT(m_factory
) );
1115 bool wxMenu::GtkAppend(wxMenuItem
*mitem
)
1117 GtkWidget
*menuItem
;
1119 #if defined(USE_MENU_BITMAPS) || !GTK_CHECK_VERSION(1, 2, 0)
1120 bool appended
= FALSE
;
1123 // does this item terminate the current radio group?
1124 bool endOfRadioGroup
= TRUE
;
1126 if ( mitem
->IsSeparator() )
1128 GtkItemFactoryEntry entry
;
1129 entry
.path
= (char *)"/sep";
1130 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
1131 entry
.callback_action
= 0;
1132 entry
.item_type
= (char *)"<Separator>";
1133 entry
.accelerator
= (gchar
*) NULL
;
1135 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
1137 // this will be wrong for more than one separator. do we care?
1138 menuItem
= gtk_item_factory_get_widget( m_factory
, "<main>/sep" );
1140 // we might have a separator inside a radio group
1141 endOfRadioGroup
= FALSE
;
1143 else if ( mitem
->IsSubMenu() )
1145 // text has "_" instead of "&" after mitem->SetText()
1146 wxString
text( mitem
->GetText() );
1148 // local buffer in multibyte form
1151 strcat( buf
, wxGTK_CONV( text
) );
1153 GtkItemFactoryEntry entry
;
1155 entry
.callback
= (GtkItemFactoryCallback
) 0;
1156 entry
.callback_action
= 0;
1157 entry
.item_type
= (char *)"<Branch>";
1158 entry
.accelerator
= (gchar
*) NULL
;
1160 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
1162 wxString
path( mitem
->GetFactoryPath() );
1163 menuItem
= gtk_item_factory_get_item( m_factory
, wxGTK_CONV( path
) );
1165 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menuItem
), mitem
->GetSubMenu()->m_menu
);
1167 // if adding a submenu to a menu already existing in the menu bar, we
1168 // must set invoking window to allow processing events from this
1170 if ( m_invokingWindow
)
1171 wxMenubarSetInvokingWindow(mitem
->GetSubMenu(), m_invokingWindow
);
1173 #ifdef USE_MENU_BITMAPS
1174 else if (mitem
->GetBitmap().Ok()) // An item with bitmap
1176 wxString
text( mitem
->GetText() );
1177 const wxBitmap
*bitmap
= &mitem
->GetBitmap();
1179 menuItem
= gtk_pixmap_menu_item_new ();
1180 GtkWidget
*label
= gtk_accel_label_new ( wxGTK_CONV( text
) );
1181 gtk_misc_set_alignment (GTK_MISC (label
), 0.0, 0.5);
1182 gtk_container_add (GTK_CONTAINER (menuItem
), label
);
1183 guint accel_key
= gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV( text
) );
1184 gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (label
), menuItem
);
1185 if (accel_key
!= GDK_VoidSymbol
)
1187 gtk_widget_add_accelerator (menuItem
,
1189 gtk_menu_ensure_uline_accel_group (GTK_MENU (m_menu
)),
1193 gtk_widget_show (label
);
1195 mitem
->SetLabelWidget(label
);
1197 GtkWidget
* pixmap
= gtk_pixmap_new( bitmap
->GetPixmap(), bitmap
->GetMask() ? bitmap
->GetMask()->GetBitmap() : (GdkBitmap
* )NULL
);
1198 gtk_widget_show(pixmap
);
1199 gtk_pixmap_menu_item_set_pixmap(GTK_PIXMAP_MENU_ITEM( menuItem
), pixmap
);
1201 gtk_signal_connect( GTK_OBJECT(menuItem
), "activate",
1202 GTK_SIGNAL_FUNC(gtk_menu_clicked_callback
),
1205 gtk_menu_append( GTK_MENU(m_menu
), menuItem
);
1206 gtk_widget_show( menuItem
);
1208 appended
= TRUE
; // We've done this, don't do it again
1210 #endif // USE_MENU_BITMAPS
1211 else // a normal item
1213 // text has "_" instead of "&" after mitem->SetText() so don't use it
1214 wxString
text( mitem
->GetText() );
1216 // buffers containing the menu item path and type in multibyte form
1220 strcpy( bufPath
, "/" );
1221 strncat( bufPath
, wxGTK_CONV(text
), WXSIZEOF(bufPath
) - 2 );
1222 bufPath
[WXSIZEOF(bufPath
) - 1] = '\0';
1224 GtkItemFactoryEntry entry
;
1225 entry
.path
= bufPath
;
1226 entry
.callback
= (GtkItemFactoryCallback
) gtk_menu_clicked_callback
;
1227 entry
.callback_action
= 0;
1230 const char *item_type
;
1231 switch ( mitem
->GetKind() )
1234 item_type
= "<CheckItem>";
1238 if ( m_pathLastRadio
.empty() )
1240 // start of a new radio group
1241 item_type
= "<RadioItem>";
1242 wxString
tmp( wxGTK_CONV_BACK( bufPath
) );
1244 m_pathLastRadio
= tmp
;
1246 else // continue the radio group
1248 pathRadio
= m_pathLastRadio
;
1249 pathRadio
.Replace(wxT("_"), wxT(""));
1250 pathRadio
.Prepend(wxT("<main>/"));
1252 strncpy(bufType
, wxGTK_CONV(pathRadio
), WXSIZEOF(bufType
));
1253 bufType
[WXSIZEOF(bufType
) - 1] = '\0';
1254 item_type
= bufType
;
1257 // continue the existing radio group, if any
1258 endOfRadioGroup
= FALSE
;
1262 wxFAIL_MSG( _T("unexpected menu item kind") );
1266 item_type
= "<Item>";
1270 entry
.item_type
= (char *)item_type
; // cast needed for GTK+
1271 entry
.accelerator
= (gchar
*) NULL
;
1274 // due to an apparent bug in GTK+, we have to use a static buffer here -
1275 // otherwise GTK+ 1.2.2 manages to override the memory we pass to it
1277 char s_accel
[50]; // should be big enough, we check for overruns
1278 wxString
tmp( GetHotKey(*mitem
) );
1279 strncpy(s_accel
, wxGTK_CONV( tmp
), WXSIZEOF(s_accel
));
1280 s_accel
[WXSIZEOF(s_accel
) - 1] = '\0';
1281 entry
.accelerator
= s_accel
;
1282 #else // !wxUSE_ACCEL
1283 entry
.accelerator
= (char*) NULL
;
1284 #endif // wxUSE_ACCEL/!wxUSE_ACCEL
1286 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); /* what is 2 ? */
1288 wxString
path( mitem
->GetFactoryPath() );
1289 menuItem
= gtk_item_factory_get_widget( m_factory
, wxGTK_CONV( path
) );
1292 wxLogError( wxT("Wrong menu path: %s\n"), path
.c_str() );
1295 if ( !mitem
->IsSeparator() )
1297 wxASSERT_MSG( menuItem
, wxT("invalid menuitem") );
1299 gtk_signal_connect( GTK_OBJECT(menuItem
), "select",
1300 GTK_SIGNAL_FUNC(gtk_menu_hilight_callback
),
1303 gtk_signal_connect( GTK_OBJECT(menuItem
), "deselect",
1304 GTK_SIGNAL_FUNC(gtk_menu_nolight_callback
),
1308 mitem
->SetMenuItem(menuItem
);
1310 if ( endOfRadioGroup
)
1312 m_pathLastRadio
.clear();
1318 bool wxMenu::DoAppend(wxMenuItem
*mitem
)
1320 return GtkAppend(mitem
) && wxMenuBase::DoAppend(mitem
);
1323 bool wxMenu::DoInsert(size_t pos
, wxMenuItem
*item
)
1325 if ( !wxMenuBase::DoInsert(pos
, item
) )
1328 // GTK+ doesn't have a function to insert a menu using GtkItemFactory (as
1329 // of version 1.2.6), so we first append the item and then change its
1331 if ( !GtkAppend(item
) )
1334 if ( m_style
& wxMENU_TEAROFF
)
1336 // change the position as the first item is the tear-off marker
1340 GtkMenuShell
*menu_shell
= GTK_MENU_SHELL(m_factory
->widget
);
1341 gpointer data
= g_list_last(menu_shell
->children
)->data
;
1342 menu_shell
->children
= g_list_remove(menu_shell
->children
, data
);
1343 menu_shell
->children
= g_list_insert(menu_shell
->children
, data
, pos
);
1348 wxMenuItem
*wxMenu::DoRemove(wxMenuItem
*item
)
1350 if ( !wxMenuBase::DoRemove(item
) )
1351 return (wxMenuItem
*)NULL
;
1353 // TODO: this code doesn't delete the item factory item and this seems
1354 // impossible as of GTK 1.2.6.
1355 gtk_widget_destroy( item
->GetMenuItem() );
1360 int wxMenu::FindMenuIdByMenuItem( GtkWidget
*menuItem
) const
1362 wxMenuItemList::Node
*node
= m_items
.GetFirst();
1365 wxMenuItem
*item
= node
->GetData();
1366 if (item
->GetMenuItem() == menuItem
)
1367 return item
->GetId();
1368 node
= node
->GetNext();
1374 // ----------------------------------------------------------------------------
1376 // ----------------------------------------------------------------------------
1378 #if GTK_CHECK_VERSION(1, 2, 0) && wxUSE_ACCEL
1380 static wxString
GetHotKey( const wxMenuItem
& item
)
1384 wxAcceleratorEntry
*accel
= item
.GetAccel();
1387 int flags
= accel
->GetFlags();
1388 if ( flags
& wxACCEL_ALT
)
1389 hotkey
+= wxT("<alt>");
1390 if ( flags
& wxACCEL_CTRL
)
1391 hotkey
+= wxT("<control>");
1392 if ( flags
& wxACCEL_SHIFT
)
1393 hotkey
+= wxT("<shift>");
1395 int code
= accel
->GetKeyCode();
1410 hotkey
<< wxT('F') << code
- WXK_F1
+ 1;
1413 // TODO: we should use gdk_keyval_name() (a.k.a.
1414 // XKeysymToString) here as well as hardcoding the keysym
1415 // names this might be not portable
1416 case WXK_NUMPAD_INSERT
:
1417 hotkey
<< wxT("KP_Insert" );
1419 case WXK_NUMPAD_DELETE
:
1420 hotkey
<< wxT("KP_Delete" );
1423 hotkey
<< wxT("Insert" );
1426 hotkey
<< wxT("Delete" );
1429 hotkey
<< wxT("Up" );
1432 hotkey
<< wxT("Down" );
1435 hotkey
<< wxT("Prior" );
1438 hotkey
<< wxT("Next" );
1441 hotkey
<< wxT("Left" );
1444 hotkey
<< wxT("Right" );
1447 hotkey
<< wxT("Home" );
1450 hotkey
<< wxT("End" );
1453 hotkey
<< wxT("Return" );
1456 // if there are any other keys wxGetAccelFromString() may
1457 // return, we should process them here
1462 wxString name
= wxGTK_CONV_BACK( gdk_keyval_name((guint
)code
) );
1470 wxFAIL_MSG( wxT("unknown keyboard accel") );
1479 #endif // wxUSE_ACCEL
1482 //-----------------------------------------------------------------------------
1483 // substitute for missing GtkPixmapMenuItem
1484 //-----------------------------------------------------------------------------
1486 #ifdef USE_MENU_BITMAPS
1489 * Copyright (C) 1998, 1999, 2000 Free Software Foundation
1490 * All rights reserved.
1492 * This file is part of the Gnome Library.
1494 * The Gnome Library is free software; you can redistribute it and/or
1495 * modify it under the terms of the GNU Library General Public License as
1496 * published by the Free Software Foundation; either version 2 of the
1497 * License, or (at your option) any later version.
1499 * The Gnome Library is distributed in the hope that it will be useful,
1500 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1501 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1502 * Library General Public License for more details.
1504 * You should have received a copy of the GNU Library General Public
1505 * License along with the Gnome Library; see the file COPYING.LIB. If not,
1506 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
1507 * Boston, MA 02111-1307, USA.
1513 /* Author: Dietmar Maurer <dm@vlsivie.tuwien.ac.at> */
1515 #include <gtk/gtkaccellabel.h>
1516 #include <gtk/gtksignal.h>
1517 #include <gtk/gtkmenuitem.h>
1518 #include <gtk/gtkmenu.h>
1519 #include <gtk/gtkcontainer.h>
1524 static void gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass
*klass
);
1525 static void gtk_pixmap_menu_item_init (GtkPixmapMenuItem
*menu_item
);
1526 static void gtk_pixmap_menu_item_draw (GtkWidget
*widget
,
1527 GdkRectangle
*area
);
1528 static gint
gtk_pixmap_menu_item_expose (GtkWidget
*widget
,
1529 GdkEventExpose
*event
);
1531 /* we must override the following functions */
1533 static void gtk_pixmap_menu_item_map (GtkWidget
*widget
);
1534 static void gtk_pixmap_menu_item_size_allocate (GtkWidget
*widget
,
1535 GtkAllocation
*allocation
);
1536 static void gtk_pixmap_menu_item_forall (GtkContainer
*container
,
1537 gboolean include_internals
,
1538 GtkCallback callback
,
1539 gpointer callback_data
);
1540 static void gtk_pixmap_menu_item_size_request (GtkWidget
*widget
,
1541 GtkRequisition
*requisition
);
1542 static void gtk_pixmap_menu_item_remove (GtkContainer
*container
,
1545 static void changed_have_pixmap_status (GtkPixmapMenuItem
*menu_item
);
1547 static GtkMenuItemClass
*parent_class
= NULL
;
1551 #define BORDER_SPACING 3
1552 #define PMAP_WIDTH 20
1555 gtk_pixmap_menu_item_get_type (void)
1557 static GtkType pixmap_menu_item_type
= 0;
1559 if (!pixmap_menu_item_type
)
1561 GtkTypeInfo pixmap_menu_item_info
=
1563 (char *)"GtkPixmapMenuItem",
1564 sizeof (GtkPixmapMenuItem
),
1565 sizeof (GtkPixmapMenuItemClass
),
1566 (GtkClassInitFunc
) gtk_pixmap_menu_item_class_init
,
1567 (GtkObjectInitFunc
) gtk_pixmap_menu_item_init
,
1568 /* reserved_1 */ NULL
,
1569 /* reserved_2 */ NULL
,
1570 (GtkClassInitFunc
) NULL
,
1573 pixmap_menu_item_type
= gtk_type_unique (gtk_menu_item_get_type (),
1574 &pixmap_menu_item_info
);
1577 return pixmap_menu_item_type
;
1581 * gtk_pixmap_menu_item_new
1583 * Creates a new pixmap menu item. Use gtk_pixmap_menu_item_set_pixmap()
1584 * to set the pixmap wich is displayed at the left side.
1587 * &GtkWidget pointer to new menu item
1591 gtk_pixmap_menu_item_new (void)
1593 return GTK_WIDGET (gtk_type_new (gtk_pixmap_menu_item_get_type ()));
1597 gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass
*klass
)
1599 GtkObjectClass
*object_class
;
1600 GtkWidgetClass
*widget_class
;
1601 GtkMenuItemClass
*menu_item_class
;
1602 GtkContainerClass
*container_class
;
1604 object_class
= (GtkObjectClass
*) klass
;
1605 widget_class
= (GtkWidgetClass
*) klass
;
1606 menu_item_class
= (GtkMenuItemClass
*) klass
;
1607 container_class
= (GtkContainerClass
*) klass
;
1609 parent_class
= (GtkMenuItemClass
*) gtk_type_class (gtk_menu_item_get_type ());
1611 widget_class
->draw
= gtk_pixmap_menu_item_draw
;
1612 widget_class
->expose_event
= gtk_pixmap_menu_item_expose
;
1613 widget_class
->map
= gtk_pixmap_menu_item_map
;
1614 widget_class
->size_allocate
= gtk_pixmap_menu_item_size_allocate
;
1615 widget_class
->size_request
= gtk_pixmap_menu_item_size_request
;
1617 container_class
->forall
= gtk_pixmap_menu_item_forall
;
1618 container_class
->remove
= gtk_pixmap_menu_item_remove
;
1620 klass
->orig_toggle_size
= menu_item_class
->toggle_size
;
1621 klass
->have_pixmap_count
= 0;
1625 gtk_pixmap_menu_item_init (GtkPixmapMenuItem
*menu_item
)
1629 mi
= GTK_MENU_ITEM (menu_item
);
1631 menu_item
->pixmap
= NULL
;
1635 gtk_pixmap_menu_item_draw (GtkWidget
*widget
,
1638 g_return_if_fail (widget
!= NULL
);
1639 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
));
1640 g_return_if_fail (area
!= NULL
);
1642 if (GTK_WIDGET_CLASS (parent_class
)->draw
)
1643 (* GTK_WIDGET_CLASS (parent_class
)->draw
) (widget
, area
);
1645 if (GTK_WIDGET_DRAWABLE (widget
) &&
1646 GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
) {
1647 gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
),NULL
);
1652 gtk_pixmap_menu_item_expose (GtkWidget
*widget
,
1653 GdkEventExpose
*event
)
1655 g_return_val_if_fail (widget
!= NULL
, FALSE
);
1656 g_return_val_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
), FALSE
);
1657 g_return_val_if_fail (event
!= NULL
, FALSE
);
1659 if (GTK_WIDGET_CLASS (parent_class
)->expose_event
)
1660 (* GTK_WIDGET_CLASS (parent_class
)->expose_event
) (widget
, event
);
1662 if (GTK_WIDGET_DRAWABLE (widget
) &&
1663 GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
) {
1664 gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
),NULL
);
1671 * gtk_pixmap_menu_item_set_pixmap
1672 * @menu_item: Pointer to the pixmap menu item
1673 * @pixmap: Pointer to a pixmap widget
1675 * Set the pixmap of the menu item.
1680 gtk_pixmap_menu_item_set_pixmap (GtkPixmapMenuItem
*menu_item
,
1683 g_return_if_fail (menu_item
!= NULL
);
1684 g_return_if_fail (pixmap
!= NULL
);
1685 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (menu_item
));
1686 g_return_if_fail (GTK_IS_WIDGET (pixmap
));
1687 g_return_if_fail (menu_item
->pixmap
== NULL
);
1689 gtk_widget_set_parent (pixmap
, GTK_WIDGET (menu_item
));
1690 menu_item
->pixmap
= pixmap
;
1692 if (GTK_WIDGET_REALIZED (pixmap
->parent
) &&
1693 !GTK_WIDGET_REALIZED (pixmap
))
1694 gtk_widget_realize (pixmap
);
1696 if (GTK_WIDGET_VISIBLE (pixmap
->parent
)) {
1697 if (GTK_WIDGET_MAPPED (pixmap
->parent
) &&
1698 GTK_WIDGET_VISIBLE(pixmap
) &&
1699 !GTK_WIDGET_MAPPED (pixmap
))
1700 gtk_widget_map (pixmap
);
1703 changed_have_pixmap_status(menu_item
);
1705 if (GTK_WIDGET_VISIBLE (pixmap
) && GTK_WIDGET_VISIBLE (menu_item
))
1706 gtk_widget_queue_resize (pixmap
);
1710 gtk_pixmap_menu_item_map (GtkWidget
*widget
)
1712 GtkPixmapMenuItem
*menu_item
;
1714 g_return_if_fail (widget
!= NULL
);
1715 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
));
1717 menu_item
= GTK_PIXMAP_MENU_ITEM(widget
);
1719 GTK_WIDGET_CLASS(parent_class
)->map(widget
);
1721 if (menu_item
->pixmap
&&
1722 GTK_WIDGET_VISIBLE (menu_item
->pixmap
) &&
1723 !GTK_WIDGET_MAPPED (menu_item
->pixmap
))
1724 gtk_widget_map (menu_item
->pixmap
);
1728 gtk_pixmap_menu_item_size_allocate (GtkWidget
*widget
,
1729 GtkAllocation
*allocation
)
1731 GtkPixmapMenuItem
*pmenu_item
;
1733 pmenu_item
= GTK_PIXMAP_MENU_ITEM(widget
);
1735 if (pmenu_item
->pixmap
&& GTK_WIDGET_VISIBLE(pmenu_item
))
1737 GtkAllocation child_allocation
;
1740 border_width
= GTK_CONTAINER (widget
)->border_width
;
1742 child_allocation
.width
= pmenu_item
->pixmap
->requisition
.width
;
1743 child_allocation
.height
= pmenu_item
->pixmap
->requisition
.height
;
1744 child_allocation
.x
= border_width
+ BORDER_SPACING
;
1745 child_allocation
.y
= (border_width
+ BORDER_SPACING
1746 + (((allocation
->height
- child_allocation
.height
) - child_allocation
.x
)
1747 / 2)); /* center pixmaps vertically */
1748 gtk_widget_size_allocate (pmenu_item
->pixmap
, &child_allocation
);
1751 if (GTK_WIDGET_CLASS (parent_class
)->size_allocate
)
1752 GTK_WIDGET_CLASS(parent_class
)->size_allocate (widget
, allocation
);
1756 gtk_pixmap_menu_item_forall (GtkContainer
*container
,
1757 gboolean include_internals
,
1758 GtkCallback callback
,
1759 gpointer callback_data
)
1761 GtkPixmapMenuItem
*menu_item
;
1763 g_return_if_fail (container
!= NULL
);
1764 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container
));
1765 g_return_if_fail (callback
!= NULL
);
1767 menu_item
= GTK_PIXMAP_MENU_ITEM (container
);
1769 if (menu_item
->pixmap
)
1770 (* callback
) (menu_item
->pixmap
, callback_data
);
1772 GTK_CONTAINER_CLASS(parent_class
)->forall(container
,include_internals
,
1773 callback
,callback_data
);
1777 gtk_pixmap_menu_item_size_request (GtkWidget
*widget
,
1778 GtkRequisition
*requisition
)
1780 GtkPixmapMenuItem
*menu_item
;
1781 GtkRequisition req
= {0, 0};
1783 g_return_if_fail (widget
!= NULL
);
1784 g_return_if_fail (GTK_IS_MENU_ITEM (widget
));
1785 g_return_if_fail (requisition
!= NULL
);
1787 GTK_WIDGET_CLASS(parent_class
)->size_request(widget
,requisition
);
1789 menu_item
= GTK_PIXMAP_MENU_ITEM (widget
);
1791 if (menu_item
->pixmap
)
1792 gtk_widget_size_request(menu_item
->pixmap
, &req
);
1794 requisition
->height
= MAX(req
.height
+ GTK_CONTAINER(widget
)->border_width
+ BORDER_SPACING
, (unsigned int) requisition
->height
);
1795 requisition
->width
+= (req
.width
+ GTK_CONTAINER(widget
)->border_width
+ BORDER_SPACING
);
1799 gtk_pixmap_menu_item_remove (GtkContainer
*container
,
1803 gboolean widget_was_visible
;
1805 g_return_if_fail (container
!= NULL
);
1806 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container
));
1807 g_return_if_fail (child
!= NULL
);
1808 g_return_if_fail (GTK_IS_WIDGET (child
));
1810 bin
= GTK_BIN (container
);
1811 g_return_if_fail ((bin
->child
== child
||
1812 (GTK_PIXMAP_MENU_ITEM(container
)->pixmap
== child
)));
1814 widget_was_visible
= GTK_WIDGET_VISIBLE (child
);
1816 gtk_widget_unparent (child
);
1817 if (bin
->child
== child
)
1820 GTK_PIXMAP_MENU_ITEM(container
)->pixmap
= NULL
;
1821 changed_have_pixmap_status(GTK_PIXMAP_MENU_ITEM(container
));
1824 if (widget_was_visible
)
1825 gtk_widget_queue_resize (GTK_WIDGET (container
));
1829 /* important to only call this if there was actually a _change_ in pixmap == NULL */
1831 changed_have_pixmap_status (GtkPixmapMenuItem
*menu_item
)
1833 if (menu_item
->pixmap
!= NULL
) {
1834 GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
+= 1;
1836 if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
== 1) {
1837 /* Install pixmap toggle size */
1838 GTK_MENU_ITEM_GET_CLASS(menu_item
)->toggle_size
= MAX(GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->orig_toggle_size
, PMAP_WIDTH
);
1841 GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
-= 1;
1843 if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
== 0) {
1844 /* Install normal toggle size */
1845 GTK_MENU_ITEM_GET_CLASS(menu_item
)->toggle_size
= GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->orig_toggle_size
;
1849 /* Note that we actually need to do this for _all_ GtkPixmapMenuItem
1850 whenever the klass->toggle_size changes; but by doing it anytime
1851 this function is called, we get the same effect, just because of
1852 how the preferences option to show pixmaps works. Bogus, broken.
1854 if (GTK_WIDGET_VISIBLE(GTK_WIDGET(menu_item
)))
1855 gtk_widget_queue_resize(GTK_WIDGET(menu_item
));
1858 #endif // USE_MENU_BITMAPS