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 // Is this menu on a menubar? (possibly nested)
705 wxFrame
* frame
= NULL
;
707 while ( pm
&& !frame
)
709 if ( pm
->IsAttached() )
710 frame
= pm
->GetMenuBar()->GetFrame();
712 pm
= pm
->GetParent();
715 // If it is then let the frame send the event
718 frame
->ProcessCommand(id
);
720 // otherwise let the menu have it
723 wxMenuItem
* item
= menu
->FindChildItem( id
);
724 wxCHECK_RET( item
, wxT("error in menu item callback") );
726 if (item
->IsCheckable())
728 bool isReallyChecked
= item
->IsChecked(),
729 isInternallyChecked
= item
->wxMenuItemBase::IsChecked();
731 // ensure that the internal state is always consistent with what is
732 // shown on the screen
733 item
->wxMenuItemBase::Check(isReallyChecked
);
735 // we must not report the events for the radio button going up nor the
736 // events resulting from the calls to wxMenuItem::Check()
737 if ( (item
->GetKind() == wxITEM_RADIO
&& !isReallyChecked
) ||
738 (isInternallyChecked
== isReallyChecked
) )
743 // the user pressed on the menu item: report the event below
746 menu
->SendEvent(id
, item
->IsCheckable() ? item
->IsChecked() : -1);
750 //-----------------------------------------------------------------------------
752 //-----------------------------------------------------------------------------
754 static void gtk_menu_hilight_callback( GtkWidget
*widget
, wxMenu
*menu
)
756 if (g_isIdle
) wxapp_install_idle_handler();
758 int id
= menu
->FindMenuIdByMenuItem(widget
);
760 wxASSERT( id
!= -1 ); // should find it!
762 if (!menu
->IsEnabled(id
))
765 wxMenuEvent
event( wxEVT_MENU_HIGHLIGHT
, id
);
766 event
.SetEventObject( menu
);
768 wxEvtHandler
* handler
= menu
->GetEventHandler();
769 if (handler
&& handler
->ProcessEvent(event
))
772 wxWindow
*win
= menu
->GetInvokingWindow();
773 if (win
) win
->GetEventHandler()->ProcessEvent( event
);
776 //-----------------------------------------------------------------------------
778 //-----------------------------------------------------------------------------
780 static void gtk_menu_nolight_callback( GtkWidget
*widget
, wxMenu
*menu
)
782 if (g_isIdle
) wxapp_install_idle_handler();
784 int id
= menu
->FindMenuIdByMenuItem(widget
);
786 wxASSERT( id
!= -1 ); // should find it!
788 if (!menu
->IsEnabled(id
))
791 wxMenuEvent
event( wxEVT_MENU_HIGHLIGHT
, -1 );
792 event
.SetEventObject( menu
);
794 wxEvtHandler
* handler
= menu
->GetEventHandler();
795 if (handler
&& handler
->ProcessEvent(event
))
798 wxWindow
*win
= menu
->GetInvokingWindow();
800 win
->GetEventHandler()->ProcessEvent( event
);
803 //-----------------------------------------------------------------------------
805 //-----------------------------------------------------------------------------
807 IMPLEMENT_DYNAMIC_CLASS(wxMenuItem
, wxObject
)
809 wxMenuItem
*wxMenuItemBase::New(wxMenu
*parentMenu
,
811 const wxString
& name
,
812 const wxString
& help
,
816 return new wxMenuItem(parentMenu
, id
, name
, help
, kind
, subMenu
);
819 wxMenuItem::wxMenuItem(wxMenu
*parentMenu
,
821 const wxString
& text
,
822 const wxString
& help
,
825 : wxMenuItemBase(parentMenu
, id
, text
, help
, kind
, subMenu
)
830 wxMenuItem::wxMenuItem(wxMenu
*parentMenu
,
832 const wxString
& text
,
833 const wxString
& help
,
836 : wxMenuItemBase(parentMenu
, id
, text
, help
,
837 isCheckable
? wxITEM_CHECK
: wxITEM_NORMAL
, subMenu
)
842 void wxMenuItem::Init(const wxString
& text
)
844 m_labelWidget
= (GtkWidget
*) NULL
;
845 m_menuItem
= (GtkWidget
*) NULL
;
850 wxMenuItem::~wxMenuItem()
852 // don't delete menu items, the menus take care of that
855 // return the menu item text without any menu accels
857 wxString
wxMenuItemBase::GetLabelFromText(const wxString
& text
)
861 for ( const wxChar
*pc
= text
.c_str(); *pc
; pc
++ )
863 if ( *pc
== wxT('_') )
865 // GTK 1.2 escapes "xxx_xxx" to "xxx__xxx"
871 #if GTK_CHECK_VERSION(2, 0, 0)
872 if ( *pc
== wxT('\\') )
874 // GTK 2.0 escapes "xxx/xxx" to "xxx\/xxx"
881 if ( (*pc
== wxT('&')) && (*(pc
+1) != wxT('&')) )
884 // "&" is doubled to indicate "&" instead of accelerator
891 // wxPrintf( L"text %s label %s\n", text.c_str(), label.c_str() );
896 void wxMenuItem::SetText( const wxString
& str
)
898 // Some optimization to avoid flicker
899 wxString oldLabel
= m_text
;
900 oldLabel
= wxStripMenuCodes(oldLabel
.BeforeFirst('\t'));
901 oldLabel
.Replace(wxT("_"), wxT(""));
902 wxString label1
= wxStripMenuCodes(str
.BeforeFirst('\t'));
903 if (oldLabel
== label1
)
912 label
= (GtkLabel
*) m_labelWidget
;
914 label
= GTK_LABEL( GTK_BIN(m_menuItem
)->child
);
916 #if GTK_CHECK_VERSION(2, 0, 0)
917 // We have to imitate item_factory_unescape_label here
919 for (size_t n
= 0; n
< m_text
.Len(); n
++)
921 if (m_text
[n
] != wxT('\\'))
925 gtk_label_set_text_with_mnemonic( GTK_LABEL(label
), wxGTK_CONV(tmp
) );
928 gtk_label_set( label
, wxGTK_CONV( m_text
) );
931 (void)gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV(m_text
) );
932 gtk_accel_label_refetch( GTK_ACCEL_LABEL(label
) );
937 // it's valid for this function to be called even if m_menuItem == NULL
938 void wxMenuItem::DoSetText( const wxString
& str
)
940 // '\t' is the deliminator indicating a hot key
942 const wxChar
*pc
= str
;
943 while ( (*pc
!= wxT('\0')) && (*pc
!= wxT('\t')) )
945 if ((*pc
== wxT('&')) && (*(pc
+1) == wxT('&')))
947 // "&" is doubled to indicate "&" instead of accelerator
951 else if (*pc
== wxT('&'))
955 #if GTK_CHECK_VERSION(2, 0, 0)
956 else if ( *pc
== wxT('_') ) // escape underscores
958 // m_text << wxT("__"); doesn't work
961 else if (*pc
== wxT('/')) // we have to escape slashes
963 m_text
<< wxT("\\/");
965 else if (*pc
== wxT('\\')) // we have to double backslashes
967 m_text
<< wxT("\\\\");
970 else if ( *pc
== wxT('_') ) // escape underscores
974 else if (*pc
== wxT('/')) /* we have to filter out slashes ... */
976 m_text
<< wxT('\\'); /* ... and replace them with back slashes */
985 // wxPrintf( L"str %s m_text %s\n", str.c_str(), m_text.c_str() );
998 wxAcceleratorEntry
*wxMenuItem::GetAccel() const
1003 return (wxAcceleratorEntry
*)NULL
;
1006 // as wxGetAccelFromString() looks for TAB, insert a dummy one here
1008 label
<< wxT('\t') << GetHotKey();
1010 return wxGetAccelFromString(label
);
1013 #endif // wxUSE_ACCEL
1015 void wxMenuItem::Check( bool check
)
1017 wxCHECK_RET( m_menuItem
, wxT("invalid menu item") );
1019 if (check
== m_isChecked
)
1022 wxMenuItemBase::Check( check
);
1024 switch ( GetKind() )
1028 gtk_check_menu_item_set_state( (GtkCheckMenuItem
*)m_menuItem
, (gint
)check
);
1032 wxFAIL_MSG( _T("can't check this item") );
1036 void wxMenuItem::Enable( bool enable
)
1038 wxCHECK_RET( m_menuItem
, wxT("invalid menu item") );
1040 gtk_widget_set_sensitive( m_menuItem
, enable
);
1041 wxMenuItemBase::Enable( enable
);
1044 bool wxMenuItem::IsChecked() const
1046 wxCHECK_MSG( m_menuItem
, FALSE
, wxT("invalid menu item") );
1048 wxCHECK_MSG( IsCheckable(), FALSE
,
1049 wxT("can't get state of uncheckable item!") );
1051 return ((GtkCheckMenuItem
*)m_menuItem
)->active
!= 0;
1054 wxString
wxMenuItem::GetFactoryPath() const
1056 // In order to get the pointer to the item we need the item
1057 // text _without_ underscores in GTK 1.2
1058 wxString
path( wxT("<main>/") );
1060 for ( const wxChar
*pc
= m_text
.c_str(); *pc
; pc
++ )
1062 if ( *pc
== wxT('_') )
1067 // remove '_' unconditionally
1072 // don't remove ampersands '&' since if we have them in the menu item title
1073 // it means that they were doubled to indicate "&" instead of accelerator
1081 //-----------------------------------------------------------------------------
1083 //-----------------------------------------------------------------------------
1085 IMPLEMENT_DYNAMIC_CLASS(wxMenu
,wxEvtHandler
)
1089 m_accel
= gtk_accel_group_new();
1090 m_factory
= gtk_item_factory_new( GTK_TYPE_MENU
, "<main>", m_accel
);
1091 m_menu
= gtk_item_factory_get_widget( m_factory
, "<main>" );
1093 m_owner
= (GtkWidget
*) NULL
;
1095 // Tearoffs are entries, just like separators. So if we want this
1096 // menu to be a tear-off one, we just append a tearoff entry
1098 if(m_style
& wxMENU_TEAROFF
)
1100 GtkItemFactoryEntry entry
;
1101 entry
.path
= (char *)"/tearoff";
1102 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
1103 entry
.callback_action
= 0;
1104 entry
.item_type
= (char *)"<Tearoff>";
1105 entry
.accelerator
= (gchar
*) NULL
;
1106 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
1107 //GtkWidget *menuItem = gtk_item_factory_get_widget( m_factory, "<main>/tearoff" );
1110 // append the title as the very first entry if we have it
1113 Append(-2, m_title
);
1122 gtk_widget_destroy( m_menu
);
1124 gtk_object_unref( GTK_OBJECT(m_factory
) );
1127 bool wxMenu::GtkAppend(wxMenuItem
*mitem
)
1129 GtkWidget
*menuItem
;
1131 #if defined(USE_MENU_BITMAPS) || !GTK_CHECK_VERSION(1, 2, 0)
1132 bool appended
= FALSE
;
1135 // does this item terminate the current radio group?
1136 bool endOfRadioGroup
= TRUE
;
1138 if ( mitem
->IsSeparator() )
1140 GtkItemFactoryEntry entry
;
1141 entry
.path
= (char *)"/sep";
1142 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
1143 entry
.callback_action
= 0;
1144 entry
.item_type
= (char *)"<Separator>";
1145 entry
.accelerator
= (gchar
*) NULL
;
1147 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
1149 // this will be wrong for more than one separator. do we care?
1150 menuItem
= gtk_item_factory_get_widget( m_factory
, "<main>/sep" );
1152 // we might have a separator inside a radio group
1153 endOfRadioGroup
= FALSE
;
1155 else if ( mitem
->IsSubMenu() )
1157 // text has "_" instead of "&" after mitem->SetText()
1158 wxString
text( mitem
->GetText() );
1160 // local buffer in multibyte form
1163 strcat( buf
, wxGTK_CONV( text
) );
1165 GtkItemFactoryEntry entry
;
1167 entry
.callback
= (GtkItemFactoryCallback
) 0;
1168 entry
.callback_action
= 0;
1169 entry
.item_type
= (char *)"<Branch>";
1170 entry
.accelerator
= (gchar
*) NULL
;
1172 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
1174 wxString
path( mitem
->GetFactoryPath() );
1175 menuItem
= gtk_item_factory_get_item( m_factory
, wxGTK_CONV( path
) );
1177 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menuItem
), mitem
->GetSubMenu()->m_menu
);
1179 // if adding a submenu to a menu already existing in the menu bar, we
1180 // must set invoking window to allow processing events from this
1182 if ( m_invokingWindow
)
1183 wxMenubarSetInvokingWindow(mitem
->GetSubMenu(), m_invokingWindow
);
1185 #ifdef USE_MENU_BITMAPS
1186 else if (mitem
->GetBitmap().Ok()) // An item with bitmap
1188 wxString
text( mitem
->GetText() );
1189 const wxBitmap
*bitmap
= &mitem
->GetBitmap();
1191 menuItem
= gtk_pixmap_menu_item_new ();
1192 GtkWidget
*label
= gtk_accel_label_new ( wxGTK_CONV( text
) );
1193 gtk_misc_set_alignment (GTK_MISC (label
), 0.0, 0.5);
1194 gtk_container_add (GTK_CONTAINER (menuItem
), label
);
1195 guint accel_key
= gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV( text
) );
1196 gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (label
), menuItem
);
1197 if (accel_key
!= GDK_VoidSymbol
)
1199 gtk_widget_add_accelerator (menuItem
,
1201 gtk_menu_ensure_uline_accel_group (GTK_MENU (m_menu
)),
1205 gtk_widget_show (label
);
1207 mitem
->SetLabelWidget(label
);
1209 GtkWidget
* pixmap
= gtk_pixmap_new( bitmap
->GetPixmap(), bitmap
->GetMask() ? bitmap
->GetMask()->GetBitmap() : (GdkBitmap
* )NULL
);
1210 gtk_widget_show(pixmap
);
1211 gtk_pixmap_menu_item_set_pixmap(GTK_PIXMAP_MENU_ITEM( menuItem
), pixmap
);
1213 gtk_signal_connect( GTK_OBJECT(menuItem
), "activate",
1214 GTK_SIGNAL_FUNC(gtk_menu_clicked_callback
),
1217 gtk_menu_append( GTK_MENU(m_menu
), menuItem
);
1218 gtk_widget_show( menuItem
);
1220 appended
= TRUE
; // We've done this, don't do it again
1222 #endif // USE_MENU_BITMAPS
1223 else // a normal item
1225 // text has "_" instead of "&" after mitem->SetText() so don't use it
1226 wxString
text( mitem
->GetText() );
1228 // buffers containing the menu item path and type in multibyte form
1232 strcpy( bufPath
, "/" );
1233 strncat( bufPath
, wxGTK_CONV(text
), WXSIZEOF(bufPath
) - 2 );
1234 bufPath
[WXSIZEOF(bufPath
) - 1] = '\0';
1236 GtkItemFactoryEntry entry
;
1237 entry
.path
= bufPath
;
1238 entry
.callback
= (GtkItemFactoryCallback
) gtk_menu_clicked_callback
;
1239 entry
.callback_action
= 0;
1242 const char *item_type
;
1243 switch ( mitem
->GetKind() )
1246 item_type
= "<CheckItem>";
1250 if ( m_pathLastRadio
.empty() )
1252 // start of a new radio group
1253 item_type
= "<RadioItem>";
1254 wxString
tmp( wxGTK_CONV_BACK( bufPath
) );
1256 m_pathLastRadio
= tmp
;
1258 else // continue the radio group
1260 pathRadio
= m_pathLastRadio
;
1261 pathRadio
.Replace(wxT("_"), wxT(""));
1262 pathRadio
.Prepend(wxT("<main>/"));
1264 strncpy(bufType
, wxGTK_CONV(pathRadio
), WXSIZEOF(bufType
));
1265 bufType
[WXSIZEOF(bufType
) - 1] = '\0';
1266 item_type
= bufType
;
1269 // continue the existing radio group, if any
1270 endOfRadioGroup
= FALSE
;
1274 wxFAIL_MSG( _T("unexpected menu item kind") );
1278 item_type
= "<Item>";
1282 entry
.item_type
= (char *)item_type
; // cast needed for GTK+
1283 entry
.accelerator
= (gchar
*) NULL
;
1286 // due to an apparent bug in GTK+, we have to use a static buffer here -
1287 // otherwise GTK+ 1.2.2 manages to override the memory we pass to it
1289 char s_accel
[50]; // should be big enough, we check for overruns
1290 wxString
tmp( GetHotKey(*mitem
) );
1291 strncpy(s_accel
, wxGTK_CONV( tmp
), WXSIZEOF(s_accel
));
1292 s_accel
[WXSIZEOF(s_accel
) - 1] = '\0';
1293 entry
.accelerator
= s_accel
;
1294 #else // !wxUSE_ACCEL
1295 entry
.accelerator
= (char*) NULL
;
1296 #endif // wxUSE_ACCEL/!wxUSE_ACCEL
1298 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); /* what is 2 ? */
1300 wxString
path( mitem
->GetFactoryPath() );
1301 menuItem
= gtk_item_factory_get_widget( m_factory
, wxGTK_CONV( path
) );
1304 wxLogError( wxT("Wrong menu path: %s\n"), path
.c_str() );
1307 if ( !mitem
->IsSeparator() )
1309 wxASSERT_MSG( menuItem
, wxT("invalid menuitem") );
1311 gtk_signal_connect( GTK_OBJECT(menuItem
), "select",
1312 GTK_SIGNAL_FUNC(gtk_menu_hilight_callback
),
1315 gtk_signal_connect( GTK_OBJECT(menuItem
), "deselect",
1316 GTK_SIGNAL_FUNC(gtk_menu_nolight_callback
),
1320 mitem
->SetMenuItem(menuItem
);
1322 if ( endOfRadioGroup
)
1324 m_pathLastRadio
.clear();
1330 bool wxMenu::DoAppend(wxMenuItem
*mitem
)
1332 return GtkAppend(mitem
) && wxMenuBase::DoAppend(mitem
);
1335 bool wxMenu::DoInsert(size_t pos
, wxMenuItem
*item
)
1337 if ( !wxMenuBase::DoInsert(pos
, item
) )
1340 // GTK+ doesn't have a function to insert a menu using GtkItemFactory (as
1341 // of version 1.2.6), so we first append the item and then change its
1343 if ( !GtkAppend(item
) )
1346 if ( m_style
& wxMENU_TEAROFF
)
1348 // change the position as the first item is the tear-off marker
1352 GtkMenuShell
*menu_shell
= GTK_MENU_SHELL(m_factory
->widget
);
1353 gpointer data
= g_list_last(menu_shell
->children
)->data
;
1354 menu_shell
->children
= g_list_remove(menu_shell
->children
, data
);
1355 menu_shell
->children
= g_list_insert(menu_shell
->children
, data
, pos
);
1360 wxMenuItem
*wxMenu::DoRemove(wxMenuItem
*item
)
1362 if ( !wxMenuBase::DoRemove(item
) )
1363 return (wxMenuItem
*)NULL
;
1365 // TODO: this code doesn't delete the item factory item and this seems
1366 // impossible as of GTK 1.2.6.
1367 gtk_widget_destroy( item
->GetMenuItem() );
1372 int wxMenu::FindMenuIdByMenuItem( GtkWidget
*menuItem
) const
1374 wxMenuItemList::Node
*node
= m_items
.GetFirst();
1377 wxMenuItem
*item
= node
->GetData();
1378 if (item
->GetMenuItem() == menuItem
)
1379 return item
->GetId();
1380 node
= node
->GetNext();
1386 // ----------------------------------------------------------------------------
1388 // ----------------------------------------------------------------------------
1390 #if GTK_CHECK_VERSION(1, 2, 0) && wxUSE_ACCEL
1392 static wxString
GetHotKey( const wxMenuItem
& item
)
1396 wxAcceleratorEntry
*accel
= item
.GetAccel();
1399 int flags
= accel
->GetFlags();
1400 if ( flags
& wxACCEL_ALT
)
1401 hotkey
+= wxT("<alt>");
1402 if ( flags
& wxACCEL_CTRL
)
1403 hotkey
+= wxT("<control>");
1404 if ( flags
& wxACCEL_SHIFT
)
1405 hotkey
+= wxT("<shift>");
1407 int code
= accel
->GetKeyCode();
1422 hotkey
<< wxT('F') << code
- WXK_F1
+ 1;
1425 // TODO: we should use gdk_keyval_name() (a.k.a.
1426 // XKeysymToString) here as well as hardcoding the keysym
1427 // names this might be not portable
1428 case WXK_NUMPAD_INSERT
:
1429 hotkey
<< wxT("KP_Insert" );
1431 case WXK_NUMPAD_DELETE
:
1432 hotkey
<< wxT("KP_Delete" );
1435 hotkey
<< wxT("Insert" );
1438 hotkey
<< wxT("Delete" );
1441 hotkey
<< wxT("Up" );
1444 hotkey
<< wxT("Down" );
1447 hotkey
<< wxT("Prior" );
1450 hotkey
<< wxT("Next" );
1453 hotkey
<< wxT("Left" );
1456 hotkey
<< wxT("Right" );
1459 hotkey
<< wxT("Home" );
1462 hotkey
<< wxT("End" );
1465 hotkey
<< wxT("Return" );
1468 // if there are any other keys wxGetAccelFromString() may
1469 // return, we should process them here
1474 wxString name
= wxGTK_CONV_BACK( gdk_keyval_name((guint
)code
) );
1482 wxFAIL_MSG( wxT("unknown keyboard accel") );
1491 #endif // wxUSE_ACCEL
1494 //-----------------------------------------------------------------------------
1495 // substitute for missing GtkPixmapMenuItem
1496 //-----------------------------------------------------------------------------
1498 #ifdef USE_MENU_BITMAPS
1501 * Copyright (C) 1998, 1999, 2000 Free Software Foundation
1502 * All rights reserved.
1504 * This file is part of the Gnome Library.
1506 * The Gnome Library is free software; you can redistribute it and/or
1507 * modify it under the terms of the GNU Library General Public License as
1508 * published by the Free Software Foundation; either version 2 of the
1509 * License, or (at your option) any later version.
1511 * The Gnome Library is distributed in the hope that it will be useful,
1512 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1513 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1514 * Library General Public License for more details.
1516 * You should have received a copy of the GNU Library General Public
1517 * License along with the Gnome Library; see the file COPYING.LIB. If not,
1518 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
1519 * Boston, MA 02111-1307, USA.
1525 /* Author: Dietmar Maurer <dm@vlsivie.tuwien.ac.at> */
1527 #include <gtk/gtkaccellabel.h>
1528 #include <gtk/gtksignal.h>
1529 #include <gtk/gtkmenuitem.h>
1530 #include <gtk/gtkmenu.h>
1531 #include <gtk/gtkcontainer.h>
1536 static void gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass
*klass
);
1537 static void gtk_pixmap_menu_item_init (GtkPixmapMenuItem
*menu_item
);
1538 static void gtk_pixmap_menu_item_draw (GtkWidget
*widget
,
1539 GdkRectangle
*area
);
1540 static gint
gtk_pixmap_menu_item_expose (GtkWidget
*widget
,
1541 GdkEventExpose
*event
);
1543 /* we must override the following functions */
1545 static void gtk_pixmap_menu_item_map (GtkWidget
*widget
);
1546 static void gtk_pixmap_menu_item_size_allocate (GtkWidget
*widget
,
1547 GtkAllocation
*allocation
);
1548 static void gtk_pixmap_menu_item_forall (GtkContainer
*container
,
1549 gboolean include_internals
,
1550 GtkCallback callback
,
1551 gpointer callback_data
);
1552 static void gtk_pixmap_menu_item_size_request (GtkWidget
*widget
,
1553 GtkRequisition
*requisition
);
1554 static void gtk_pixmap_menu_item_remove (GtkContainer
*container
,
1557 static void changed_have_pixmap_status (GtkPixmapMenuItem
*menu_item
);
1559 static GtkMenuItemClass
*parent_class
= NULL
;
1563 #define BORDER_SPACING 3
1564 #define PMAP_WIDTH 20
1567 gtk_pixmap_menu_item_get_type (void)
1569 static GtkType pixmap_menu_item_type
= 0;
1571 if (!pixmap_menu_item_type
)
1573 GtkTypeInfo pixmap_menu_item_info
=
1575 (char *)"GtkPixmapMenuItem",
1576 sizeof (GtkPixmapMenuItem
),
1577 sizeof (GtkPixmapMenuItemClass
),
1578 (GtkClassInitFunc
) gtk_pixmap_menu_item_class_init
,
1579 (GtkObjectInitFunc
) gtk_pixmap_menu_item_init
,
1580 /* reserved_1 */ NULL
,
1581 /* reserved_2 */ NULL
,
1582 (GtkClassInitFunc
) NULL
,
1585 pixmap_menu_item_type
= gtk_type_unique (gtk_menu_item_get_type (),
1586 &pixmap_menu_item_info
);
1589 return pixmap_menu_item_type
;
1593 * gtk_pixmap_menu_item_new
1595 * Creates a new pixmap menu item. Use gtk_pixmap_menu_item_set_pixmap()
1596 * to set the pixmap wich is displayed at the left side.
1599 * &GtkWidget pointer to new menu item
1603 gtk_pixmap_menu_item_new (void)
1605 return GTK_WIDGET (gtk_type_new (gtk_pixmap_menu_item_get_type ()));
1609 gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass
*klass
)
1611 GtkObjectClass
*object_class
;
1612 GtkWidgetClass
*widget_class
;
1613 GtkMenuItemClass
*menu_item_class
;
1614 GtkContainerClass
*container_class
;
1616 object_class
= (GtkObjectClass
*) klass
;
1617 widget_class
= (GtkWidgetClass
*) klass
;
1618 menu_item_class
= (GtkMenuItemClass
*) klass
;
1619 container_class
= (GtkContainerClass
*) klass
;
1621 parent_class
= (GtkMenuItemClass
*) gtk_type_class (gtk_menu_item_get_type ());
1623 widget_class
->draw
= gtk_pixmap_menu_item_draw
;
1624 widget_class
->expose_event
= gtk_pixmap_menu_item_expose
;
1625 widget_class
->map
= gtk_pixmap_menu_item_map
;
1626 widget_class
->size_allocate
= gtk_pixmap_menu_item_size_allocate
;
1627 widget_class
->size_request
= gtk_pixmap_menu_item_size_request
;
1629 container_class
->forall
= gtk_pixmap_menu_item_forall
;
1630 container_class
->remove
= gtk_pixmap_menu_item_remove
;
1632 klass
->orig_toggle_size
= menu_item_class
->toggle_size
;
1633 klass
->have_pixmap_count
= 0;
1637 gtk_pixmap_menu_item_init (GtkPixmapMenuItem
*menu_item
)
1641 mi
= GTK_MENU_ITEM (menu_item
);
1643 menu_item
->pixmap
= NULL
;
1647 gtk_pixmap_menu_item_draw (GtkWidget
*widget
,
1650 g_return_if_fail (widget
!= NULL
);
1651 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
));
1652 g_return_if_fail (area
!= NULL
);
1654 if (GTK_WIDGET_CLASS (parent_class
)->draw
)
1655 (* GTK_WIDGET_CLASS (parent_class
)->draw
) (widget
, area
);
1657 if (GTK_WIDGET_DRAWABLE (widget
) &&
1658 GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
) {
1659 gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
),NULL
);
1664 gtk_pixmap_menu_item_expose (GtkWidget
*widget
,
1665 GdkEventExpose
*event
)
1667 g_return_val_if_fail (widget
!= NULL
, FALSE
);
1668 g_return_val_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
), FALSE
);
1669 g_return_val_if_fail (event
!= NULL
, FALSE
);
1671 if (GTK_WIDGET_CLASS (parent_class
)->expose_event
)
1672 (* GTK_WIDGET_CLASS (parent_class
)->expose_event
) (widget
, event
);
1674 if (GTK_WIDGET_DRAWABLE (widget
) &&
1675 GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
) {
1676 gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
),NULL
);
1683 * gtk_pixmap_menu_item_set_pixmap
1684 * @menu_item: Pointer to the pixmap menu item
1685 * @pixmap: Pointer to a pixmap widget
1687 * Set the pixmap of the menu item.
1692 gtk_pixmap_menu_item_set_pixmap (GtkPixmapMenuItem
*menu_item
,
1695 g_return_if_fail (menu_item
!= NULL
);
1696 g_return_if_fail (pixmap
!= NULL
);
1697 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (menu_item
));
1698 g_return_if_fail (GTK_IS_WIDGET (pixmap
));
1699 g_return_if_fail (menu_item
->pixmap
== NULL
);
1701 gtk_widget_set_parent (pixmap
, GTK_WIDGET (menu_item
));
1702 menu_item
->pixmap
= pixmap
;
1704 if (GTK_WIDGET_REALIZED (pixmap
->parent
) &&
1705 !GTK_WIDGET_REALIZED (pixmap
))
1706 gtk_widget_realize (pixmap
);
1708 if (GTK_WIDGET_VISIBLE (pixmap
->parent
)) {
1709 if (GTK_WIDGET_MAPPED (pixmap
->parent
) &&
1710 GTK_WIDGET_VISIBLE(pixmap
) &&
1711 !GTK_WIDGET_MAPPED (pixmap
))
1712 gtk_widget_map (pixmap
);
1715 changed_have_pixmap_status(menu_item
);
1717 if (GTK_WIDGET_VISIBLE (pixmap
) && GTK_WIDGET_VISIBLE (menu_item
))
1718 gtk_widget_queue_resize (pixmap
);
1722 gtk_pixmap_menu_item_map (GtkWidget
*widget
)
1724 GtkPixmapMenuItem
*menu_item
;
1726 g_return_if_fail (widget
!= NULL
);
1727 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
));
1729 menu_item
= GTK_PIXMAP_MENU_ITEM(widget
);
1731 GTK_WIDGET_CLASS(parent_class
)->map(widget
);
1733 if (menu_item
->pixmap
&&
1734 GTK_WIDGET_VISIBLE (menu_item
->pixmap
) &&
1735 !GTK_WIDGET_MAPPED (menu_item
->pixmap
))
1736 gtk_widget_map (menu_item
->pixmap
);
1740 gtk_pixmap_menu_item_size_allocate (GtkWidget
*widget
,
1741 GtkAllocation
*allocation
)
1743 GtkPixmapMenuItem
*pmenu_item
;
1745 pmenu_item
= GTK_PIXMAP_MENU_ITEM(widget
);
1747 if (pmenu_item
->pixmap
&& GTK_WIDGET_VISIBLE(pmenu_item
))
1749 GtkAllocation child_allocation
;
1752 border_width
= GTK_CONTAINER (widget
)->border_width
;
1754 child_allocation
.width
= pmenu_item
->pixmap
->requisition
.width
;
1755 child_allocation
.height
= pmenu_item
->pixmap
->requisition
.height
;
1756 child_allocation
.x
= border_width
+ BORDER_SPACING
;
1757 child_allocation
.y
= (border_width
+ BORDER_SPACING
1758 + (((allocation
->height
- child_allocation
.height
) - child_allocation
.x
)
1759 / 2)); /* center pixmaps vertically */
1760 gtk_widget_size_allocate (pmenu_item
->pixmap
, &child_allocation
);
1763 if (GTK_WIDGET_CLASS (parent_class
)->size_allocate
)
1764 GTK_WIDGET_CLASS(parent_class
)->size_allocate (widget
, allocation
);
1768 gtk_pixmap_menu_item_forall (GtkContainer
*container
,
1769 gboolean include_internals
,
1770 GtkCallback callback
,
1771 gpointer callback_data
)
1773 GtkPixmapMenuItem
*menu_item
;
1775 g_return_if_fail (container
!= NULL
);
1776 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container
));
1777 g_return_if_fail (callback
!= NULL
);
1779 menu_item
= GTK_PIXMAP_MENU_ITEM (container
);
1781 if (menu_item
->pixmap
)
1782 (* callback
) (menu_item
->pixmap
, callback_data
);
1784 GTK_CONTAINER_CLASS(parent_class
)->forall(container
,include_internals
,
1785 callback
,callback_data
);
1789 gtk_pixmap_menu_item_size_request (GtkWidget
*widget
,
1790 GtkRequisition
*requisition
)
1792 GtkPixmapMenuItem
*menu_item
;
1793 GtkRequisition req
= {0, 0};
1795 g_return_if_fail (widget
!= NULL
);
1796 g_return_if_fail (GTK_IS_MENU_ITEM (widget
));
1797 g_return_if_fail (requisition
!= NULL
);
1799 GTK_WIDGET_CLASS(parent_class
)->size_request(widget
,requisition
);
1801 menu_item
= GTK_PIXMAP_MENU_ITEM (widget
);
1803 if (menu_item
->pixmap
)
1804 gtk_widget_size_request(menu_item
->pixmap
, &req
);
1806 requisition
->height
= MAX(req
.height
+ GTK_CONTAINER(widget
)->border_width
+ BORDER_SPACING
, (unsigned int) requisition
->height
);
1807 requisition
->width
+= (req
.width
+ GTK_CONTAINER(widget
)->border_width
+ BORDER_SPACING
);
1811 gtk_pixmap_menu_item_remove (GtkContainer
*container
,
1815 gboolean widget_was_visible
;
1817 g_return_if_fail (container
!= NULL
);
1818 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container
));
1819 g_return_if_fail (child
!= NULL
);
1820 g_return_if_fail (GTK_IS_WIDGET (child
));
1822 bin
= GTK_BIN (container
);
1823 g_return_if_fail ((bin
->child
== child
||
1824 (GTK_PIXMAP_MENU_ITEM(container
)->pixmap
== child
)));
1826 widget_was_visible
= GTK_WIDGET_VISIBLE (child
);
1828 gtk_widget_unparent (child
);
1829 if (bin
->child
== child
)
1832 GTK_PIXMAP_MENU_ITEM(container
)->pixmap
= NULL
;
1833 changed_have_pixmap_status(GTK_PIXMAP_MENU_ITEM(container
));
1836 if (widget_was_visible
)
1837 gtk_widget_queue_resize (GTK_WIDGET (container
));
1841 /* important to only call this if there was actually a _change_ in pixmap == NULL */
1843 changed_have_pixmap_status (GtkPixmapMenuItem
*menu_item
)
1845 if (menu_item
->pixmap
!= NULL
) {
1846 GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
+= 1;
1848 if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
== 1) {
1849 /* Install pixmap toggle size */
1850 GTK_MENU_ITEM_GET_CLASS(menu_item
)->toggle_size
= MAX(GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->orig_toggle_size
, PMAP_WIDTH
);
1853 GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
-= 1;
1855 if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
== 0) {
1856 /* Install normal toggle size */
1857 GTK_MENU_ITEM_GET_CLASS(menu_item
)->toggle_size
= GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->orig_toggle_size
;
1861 /* Note that we actually need to do this for _all_ GtkPixmapMenuItem
1862 whenever the klass->toggle_size changes; but by doing it anytime
1863 this function is called, we get the same effect, just because of
1864 how the preferences option to show pixmaps works. Bogus, broken.
1866 if (GTK_WIDGET_VISIBLE(GTK_WIDGET(menu_item
)))
1867 gtk_widget_queue_resize(GTK_WIDGET(menu_item
));
1870 #endif // USE_MENU_BITMAPS