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, menu
);
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 /* GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has. */
204 #if GTK_CHECK_VERSION(1, 2, 1)
205 m_accel
= gtk_accel_group_new();
206 m_factory
= gtk_item_factory_new( GTK_TYPE_MENU_BAR
, "<main>", m_accel
);
207 m_menubar
= gtk_item_factory_get_widget( m_factory
, "<main>" );
209 m_menubar
= gtk_menu_bar_new();
212 if (style
& wxMB_DOCKABLE
)
214 m_widget
= gtk_handle_box_new();
215 gtk_container_add( GTK_CONTAINER(m_widget
), GTK_WIDGET(m_menubar
) );
216 gtk_widget_show( GTK_WIDGET(m_menubar
) );
220 m_widget
= GTK_WIDGET(m_menubar
);
228 wxMenuBar::wxMenuBar()
230 /* the parent window is known after wxFrame::SetMenu() */
231 m_needParent
= FALSE
;
233 m_invokingWindow
= (wxWindow
*) NULL
;
235 if (!PreCreation( (wxWindow
*) NULL
, wxDefaultPosition
, wxDefaultSize
) ||
236 !CreateBase( (wxWindow
*) NULL
, -1, wxDefaultPosition
, wxDefaultSize
, 0, wxDefaultValidator
, wxT("menubar") ))
238 wxFAIL_MSG( wxT("wxMenuBar creation failed") );
242 /* GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has. */
243 #if GTK_CHECK_VERSION(1, 2, 1)
244 m_accel
= gtk_accel_group_new();
245 m_factory
= gtk_item_factory_new( GTK_TYPE_MENU_BAR
, "<main>", m_accel
);
246 m_menubar
= gtk_item_factory_get_widget( m_factory
, "<main>" );
248 m_menubar
= gtk_menu_bar_new();
251 m_widget
= GTK_WIDGET(m_menubar
);
258 wxMenuBar::~wxMenuBar()
260 // gtk_object_unref( GTK_OBJECT(m_factory) ); why not ?
263 static void wxMenubarUnsetInvokingWindow( wxMenu
*menu
, wxWindow
*win
)
265 menu
->SetInvokingWindow( (wxWindow
*) NULL
);
267 wxWindow
*top_frame
= win
;
268 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
269 top_frame
= top_frame
->GetParent();
271 /* support for native hot keys */
272 gtk_accel_group_detach( menu
->m_accel
, ACCEL_OBJ_CAST(top_frame
->m_widget
) );
274 wxMenuItemList::compatibility_iterator node
= menu
->GetMenuItems().GetFirst();
277 wxMenuItem
*menuitem
= node
->GetData();
278 if (menuitem
->IsSubMenu())
279 wxMenubarUnsetInvokingWindow( menuitem
->GetSubMenu(), win
);
280 node
= node
->GetNext();
284 static void wxMenubarSetInvokingWindow( wxMenu
*menu
, wxWindow
*win
)
286 menu
->SetInvokingWindow( win
);
288 #if GTK_CHECK_VERSION(1, 2, 1)
289 wxWindow
*top_frame
= win
;
290 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
291 top_frame
= top_frame
->GetParent();
293 /* support for native hot keys */
294 ACCEL_OBJECT
*obj
= ACCEL_OBJ_CAST(top_frame
->m_widget
);
295 if ( !g_slist_find( ACCEL_OBJECTS(menu
->m_accel
), obj
) )
296 gtk_accel_group_attach( menu
->m_accel
, obj
);
297 #endif // GTK+ 1.2.1+
299 wxMenuItemList::compatibility_iterator node
= menu
->GetMenuItems().GetFirst();
302 wxMenuItem
*menuitem
= node
->GetData();
303 if (menuitem
->IsSubMenu())
304 wxMenubarSetInvokingWindow( menuitem
->GetSubMenu(), win
);
305 node
= node
->GetNext();
309 void wxMenuBar::SetInvokingWindow( wxWindow
*win
)
311 m_invokingWindow
= win
;
312 #if GTK_CHECK_VERSION(1, 2, 1)
313 wxWindow
*top_frame
= win
;
314 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
315 top_frame
= top_frame
->GetParent();
317 /* support for native key accelerators indicated by underscroes */
318 ACCEL_OBJECT
*obj
= ACCEL_OBJ_CAST(top_frame
->m_widget
);
319 if ( !g_slist_find( ACCEL_OBJECTS(m_accel
), obj
) )
320 gtk_accel_group_attach( m_accel
, obj
);
321 #endif // GTK+ 1.2.1+
323 wxMenuList::compatibility_iterator node
= m_menus
.GetFirst();
326 wxMenu
*menu
= node
->GetData();
327 wxMenubarSetInvokingWindow( menu
, win
);
328 node
= node
->GetNext();
332 void wxMenuBar::UnsetInvokingWindow( wxWindow
*win
)
334 m_invokingWindow
= (wxWindow
*) NULL
;
335 #if GTK_CHECK_VERSION(1, 2, 1)
336 wxWindow
*top_frame
= win
;
337 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
338 top_frame
= top_frame
->GetParent();
340 // support for native key accelerators indicated by underscroes
341 gtk_accel_group_detach( m_accel
, ACCEL_OBJ_CAST(top_frame
->m_widget
) );
342 #endif // GTK+ 1.2.1+
344 wxMenuList::compatibility_iterator node
= m_menus
.GetFirst();
347 wxMenu
*menu
= node
->GetData();
348 wxMenubarUnsetInvokingWindow( menu
, win
);
349 node
= node
->GetNext();
353 bool wxMenuBar::Append( wxMenu
*menu
, const wxString
&title
)
355 if ( !wxMenuBarBase::Append( menu
, title
) )
358 return GtkAppend(menu
, title
);
361 bool wxMenuBar::GtkAppend(wxMenu
*menu
, const wxString
& title
)
363 wxString
str( wxReplaceUnderscore( title
) );
365 // This doesn't have much effect right now.
366 menu
->SetTitle( str
);
368 // GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has.
369 #if GTK_CHECK_VERSION(1, 2, 1)
372 buf
<< wxT('/') << str
.c_str();
374 // local buffer in multibyte form
376 strcpy(cbuf
, wxGTK_CONV(buf
) );
378 GtkItemFactoryEntry entry
;
379 entry
.path
= (gchar
*)cbuf
; // const_cast
380 entry
.accelerator
= (gchar
*) NULL
;
381 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
382 entry
.callback_action
= 0;
383 entry
.item_type
= (char *)"<Branch>";
385 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
386 // in order to get the pointer to the item we need the item text _without_ underscores
387 wxString tmp
= wxT("<main>/");
389 for ( pc
= str
; *pc
!= wxT('\0'); pc
++ )
391 // contrary to the common sense, we must throw out _all_ underscores,
392 // (i.e. "Hello__World" => "HelloWorld" and not "Hello_World" as we
393 // might naively think). IMHO it's a bug in GTK+ (VZ)
394 while (*pc
== wxT('_'))
398 menu
->m_owner
= gtk_item_factory_get_item( m_factory
, wxGTK_CONV( tmp
) );
399 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu
->m_owner
), menu
->m_menu
);
402 menu
->m_owner
= gtk_menu_item_new_with_label( wxGTK_CONV( str
) );
403 gtk_widget_show( menu
->m_owner
);
404 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu
->m_owner
), menu
->m_menu
);
406 gtk_menu_bar_append( GTK_MENU_BAR(m_menubar
), menu
->m_owner
);
410 gtk_signal_connect( GTK_OBJECT(menu
->m_owner
), "activate",
411 GTK_SIGNAL_FUNC(gtk_menu_open_callback
),
414 // m_invokingWindow is set after wxFrame::SetMenuBar(). This call enables
415 // addings menu later on.
416 if (m_invokingWindow
)
418 wxMenubarSetInvokingWindow( menu
, m_invokingWindow
);
420 // OPTIMISE ME: we should probably cache this, or pass it
421 // directly, but for now this is a minimal
422 // change to validate the new dynamic sizing.
423 // see (and refactor :) similar code in Remove
426 wxFrame
*frame
= wxDynamicCast( m_invokingWindow
, wxFrame
);
429 frame
->UpdateMenuBarSize();
435 bool wxMenuBar::Insert(size_t pos
, wxMenu
*menu
, const wxString
& title
)
437 if ( !wxMenuBarBase::Insert(pos
, menu
, title
) )
440 // GTK+ doesn't have a function to insert a menu using GtkItemFactory (as
441 // of version 1.2.6), so we first append the item and then change its
443 if ( !GtkAppend(menu
, title
) )
446 if (pos
+1 >= m_menus
.GetCount())
449 GtkMenuShell
*menu_shell
= GTK_MENU_SHELL(m_factory
->widget
);
450 gpointer data
= g_list_last(menu_shell
->children
)->data
;
451 menu_shell
->children
= g_list_remove(menu_shell
->children
, data
);
452 menu_shell
->children
= g_list_insert(menu_shell
->children
, data
, pos
);
457 wxMenu
*wxMenuBar::Replace(size_t pos
, wxMenu
*menu
, const wxString
& title
)
459 // remove the old item and insert a new one
460 wxMenu
*menuOld
= Remove(pos
);
461 if ( menuOld
&& !Insert(pos
, menu
, title
) )
463 return (wxMenu
*) NULL
;
466 // either Insert() succeeded or Remove() failed and menuOld is NULL
470 static wxMenu
*CopyMenu (wxMenu
*menu
)
472 wxMenu
*menucopy
= new wxMenu ();
473 wxMenuItemList::compatibility_iterator node
= menu
->GetMenuItems().GetFirst();
476 wxMenuItem
*item
= node
->GetData();
477 int itemid
= item
->GetId();
478 wxString text
= item
->GetText();
479 text
.Replace(wxT("_"), wxT("&"));
480 wxMenu
*submenu
= item
->GetSubMenu();
483 wxMenuItem
* itemcopy
= new wxMenuItem(menucopy
,
485 menu
->GetHelpString(itemid
));
486 itemcopy
->SetBitmap(item
->GetBitmap());
487 itemcopy
->SetCheckable(item
->IsCheckable());
488 menucopy
->Append(itemcopy
);
491 menucopy
->Append (itemid
, text
, CopyMenu(submenu
),
492 menu
->GetHelpString(itemid
));
494 node
= node
->GetNext();
500 wxMenu
*wxMenuBar::Remove(size_t pos
)
502 wxMenu
*menu
= wxMenuBarBase::Remove(pos
);
504 return (wxMenu
*) NULL
;
507 GtkMenuShell *menu_shell = GTK_MENU_SHELL(m_factory->widget);
509 printf( "factory entries before %d\n", (int)g_slist_length(m_factory->items) );
510 printf( "menu shell entries before %d\n", (int)g_list_length( menu_shell->children ) );
513 wxMenu
*menucopy
= CopyMenu( menu
);
515 // unparent calls unref() and that would delete the widget so we raise
516 // the ref count to 2 artificially before invoking unparent.
517 gtk_widget_ref( menu
->m_menu
);
518 gtk_widget_unparent( menu
->m_menu
);
520 gtk_widget_destroy( menu
->m_owner
);
525 printf( "factory entries after %d\n", (int)g_slist_length(m_factory->items) );
526 printf( "menu shell entries after %d\n", (int)g_list_length( menu_shell->children ) );
529 if (m_invokingWindow
)
531 // OPTIMISE ME: see comment in GtkAppend
533 wxFrame
*frame
= wxDynamicCast( m_invokingWindow
, wxFrame
);
536 frame
->UpdateMenuBarSize();
542 static int FindMenuItemRecursive( const wxMenu
*menu
, const wxString
&menuString
, const wxString
&itemString
)
544 if (wxMenuItem::GetLabelFromText(menu
->GetTitle()) == wxMenuItem::GetLabelFromText(menuString
))
546 int res
= menu
->FindItem( itemString
);
547 if (res
!= wxNOT_FOUND
)
551 wxMenuItemList::compatibility_iterator node
= menu
->GetMenuItems().GetFirst();
554 wxMenuItem
*item
= node
->GetData();
555 if (item
->IsSubMenu())
556 return FindMenuItemRecursive(item
->GetSubMenu(), menuString
, itemString
);
558 node
= node
->GetNext();
564 int wxMenuBar::FindMenuItem( const wxString
&menuString
, const wxString
&itemString
) const
566 wxMenuList::compatibility_iterator node
= m_menus
.GetFirst();
569 wxMenu
*menu
= node
->GetData();
570 int res
= FindMenuItemRecursive( menu
, menuString
, itemString
);
573 node
= node
->GetNext();
579 // Find a wxMenuItem using its id. Recurses down into sub-menus
580 static wxMenuItem
* FindMenuItemByIdRecursive(const wxMenu
* menu
, int id
)
582 wxMenuItem
* result
= menu
->FindChildItem(id
);
584 wxMenuItemList::compatibility_iterator node
= menu
->GetMenuItems().GetFirst();
585 while ( node
&& result
== NULL
)
587 wxMenuItem
*item
= node
->GetData();
588 if (item
->IsSubMenu())
590 result
= FindMenuItemByIdRecursive( item
->GetSubMenu(), id
);
592 node
= node
->GetNext();
598 wxMenuItem
* wxMenuBar::FindItem( int id
, wxMenu
**menuForItem
) const
600 wxMenuItem
* result
= 0;
601 wxMenuList::compatibility_iterator node
= m_menus
.GetFirst();
602 while (node
&& result
== 0)
604 wxMenu
*menu
= node
->GetData();
605 result
= FindMenuItemByIdRecursive( menu
, id
);
606 node
= node
->GetNext();
611 *menuForItem
= result
? result
->GetMenu() : (wxMenu
*)NULL
;
617 void wxMenuBar::EnableTop( size_t pos
, bool flag
)
619 wxMenuList::compatibility_iterator node
= m_menus
.Item( pos
);
621 wxCHECK_RET( node
, wxT("menu not found") );
623 wxMenu
* menu
= node
->GetData();
626 gtk_widget_set_sensitive( menu
->m_owner
, flag
);
629 wxString
wxMenuBar::GetLabelTop( size_t pos
) const
631 wxMenuList::compatibility_iterator node
= m_menus
.Item( pos
);
633 wxCHECK_MSG( node
, wxT("invalid"), wxT("menu not found") );
635 wxMenu
* menu
= node
->GetData();
638 wxString
text( menu
->GetTitle() );
639 for ( const wxChar
*pc
= text
.c_str(); *pc
; pc
++ )
641 if ( *pc
== wxT('_') )
643 // '_' is the escape character for GTK+
647 // don't remove ampersands '&' since if we have them in the menu title
648 // it means that they were doubled to indicate "&" instead of accelerator
656 void wxMenuBar::SetLabelTop( size_t pos
, const wxString
& label
)
658 wxMenuList::compatibility_iterator node
= m_menus
.Item( pos
);
660 wxCHECK_RET( node
, wxT("menu not found") );
662 wxMenu
* menu
= node
->GetData();
664 wxString
str( wxReplaceUnderscore( label
) );
666 menu
->SetTitle( str
);
670 GtkLabel
*label
= GTK_LABEL( GTK_BIN(menu
->m_owner
)->child
);
673 gtk_label_set( label
, wxGTK_CONV( str
) );
675 /* reparse key accel */
676 (void)gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV( str
) );
677 gtk_accel_label_refetch( GTK_ACCEL_LABEL(label
) );
682 //-----------------------------------------------------------------------------
684 //-----------------------------------------------------------------------------
686 static void gtk_menu_clicked_callback( GtkWidget
*widget
, wxMenu
*menu
)
689 wxapp_install_idle_handler();
691 int id
= menu
->FindMenuIdByMenuItem(widget
);
693 /* should find it for normal (not popup) menu */
694 wxASSERT_MSG( (id
!= -1) || (menu
->GetInvokingWindow() != NULL
),
695 _T("menu item not found in gtk_menu_clicked_callback") );
697 if (!menu
->IsEnabled(id
))
700 wxMenuItem
* item
= menu
->FindChildItem( id
);
701 wxCHECK_RET( item
, wxT("error in menu item callback") );
703 if (item
->IsCheckable())
705 bool isReallyChecked
= item
->IsChecked(),
706 isInternallyChecked
= item
->wxMenuItemBase::IsChecked();
708 // ensure that the internal state is always consistent with what is
709 // shown on the screen
710 item
->wxMenuItemBase::Check(isReallyChecked
);
712 // we must not report the events for the radio button going up nor the
713 // events resulting from the calls to wxMenuItem::Check()
714 if ( (item
->GetKind() == wxITEM_RADIO
&& !isReallyChecked
) ||
715 (isInternallyChecked
== isReallyChecked
) )
722 // Is this menu on a menubar? (possibly nested)
723 wxFrame
* frame
= NULL
;
725 while ( pm
&& !frame
)
727 if ( pm
->IsAttached() )
728 frame
= pm
->GetMenuBar()->GetFrame();
729 pm
= pm
->GetParent();
732 // FIXME: why do we have to call wxFrame::GetEventHandler() directly here?
733 // normally wxMenu::SendEvent() should be enough, if it doesn't work
734 // in wxGTK then we have a bug in wxMenu::GetInvokingWindow() which
735 // should be fixed instead of working around it here...
738 // If it is attached then let the frame send the event.
739 // Don't call frame->ProcessCommand(id) because it toggles
740 // checkable items and we've already done that above.
741 wxCommandEvent
commandEvent(wxEVT_COMMAND_MENU_SELECTED
, id
);
742 commandEvent
.SetEventObject(frame
);
743 if (item
->IsCheckable())
744 commandEvent
.SetInt(item
->IsChecked());
745 commandEvent
.SetEventObject(menu
);
747 frame
->GetEventHandler()->ProcessEvent(commandEvent
);
751 // otherwise let the menu have it
752 menu
->SendEvent(id
, item
->IsCheckable() ? item
->IsChecked() : -1);
756 //-----------------------------------------------------------------------------
758 //-----------------------------------------------------------------------------
760 static void gtk_menu_hilight_callback( GtkWidget
*widget
, wxMenu
*menu
)
762 if (g_isIdle
) wxapp_install_idle_handler();
764 int id
= menu
->FindMenuIdByMenuItem(widget
);
766 wxASSERT( id
!= -1 ); // should find it!
768 if (!menu
->IsEnabled(id
))
771 wxMenuEvent
event( wxEVT_MENU_HIGHLIGHT
, id
);
772 event
.SetEventObject( menu
);
774 wxEvtHandler
* handler
= menu
->GetEventHandler();
775 if (handler
&& handler
->ProcessEvent(event
))
778 wxWindow
*win
= menu
->GetInvokingWindow();
779 if (win
) win
->GetEventHandler()->ProcessEvent( event
);
782 //-----------------------------------------------------------------------------
784 //-----------------------------------------------------------------------------
786 static void gtk_menu_nolight_callback( GtkWidget
*widget
, wxMenu
*menu
)
788 if (g_isIdle
) wxapp_install_idle_handler();
790 int id
= menu
->FindMenuIdByMenuItem(widget
);
792 wxASSERT( id
!= -1 ); // should find it!
794 if (!menu
->IsEnabled(id
))
797 wxMenuEvent
event( wxEVT_MENU_HIGHLIGHT
, -1 );
798 event
.SetEventObject( menu
);
800 wxEvtHandler
* handler
= menu
->GetEventHandler();
801 if (handler
&& handler
->ProcessEvent(event
))
804 wxWindow
*win
= menu
->GetInvokingWindow();
806 win
->GetEventHandler()->ProcessEvent( event
);
809 //-----------------------------------------------------------------------------
811 //-----------------------------------------------------------------------------
813 IMPLEMENT_DYNAMIC_CLASS(wxMenuItem
, wxObject
)
815 wxMenuItem
*wxMenuItemBase::New(wxMenu
*parentMenu
,
817 const wxString
& name
,
818 const wxString
& help
,
822 return new wxMenuItem(parentMenu
, id
, name
, help
, kind
, subMenu
);
825 wxMenuItem::wxMenuItem(wxMenu
*parentMenu
,
827 const wxString
& text
,
828 const wxString
& help
,
831 : wxMenuItemBase(parentMenu
, id
, text
, help
, kind
, subMenu
)
836 wxMenuItem::wxMenuItem(wxMenu
*parentMenu
,
838 const wxString
& text
,
839 const wxString
& help
,
842 : wxMenuItemBase(parentMenu
, id
, text
, help
,
843 isCheckable
? wxITEM_CHECK
: wxITEM_NORMAL
, subMenu
)
848 void wxMenuItem::Init(const wxString
& text
)
850 m_labelWidget
= (GtkWidget
*) NULL
;
851 m_menuItem
= (GtkWidget
*) NULL
;
856 wxMenuItem::~wxMenuItem()
858 // don't delete menu items, the menus take care of that
861 // return the menu item text without any menu accels
863 wxString
wxMenuItemBase::GetLabelFromText(const wxString
& text
)
867 for ( const wxChar
*pc
= text
.c_str(); *pc
; pc
++ )
869 if ( *pc
== wxT('_') )
871 // GTK 1.2 escapes "xxx_xxx" to "xxx__xxx"
877 #if GTK_CHECK_VERSION(2, 0, 0)
878 if ( *pc
== wxT('\\') )
880 // GTK 2.0 escapes "xxx/xxx" to "xxx\/xxx"
887 if ( (*pc
== wxT('&')) && (*(pc
+1) != wxT('&')) )
890 // "&" is doubled to indicate "&" instead of accelerator
897 // wxPrintf( L"text %s label %s\n", text.c_str(), label.c_str() );
902 void wxMenuItem::SetText( const wxString
& str
)
904 // Some optimization to avoid flicker
905 wxString oldLabel
= m_text
;
906 oldLabel
= wxStripMenuCodes(oldLabel
.BeforeFirst('\t'));
907 oldLabel
.Replace(wxT("_"), wxT(""));
908 wxString label1
= wxStripMenuCodes(str
.BeforeFirst('\t'));
909 if (oldLabel
== label1
)
918 label
= (GtkLabel
*) m_labelWidget
;
920 label
= GTK_LABEL( GTK_BIN(m_menuItem
)->child
);
922 #if GTK_CHECK_VERSION(2, 0, 0)
923 // We have to imitate item_factory_unescape_label here
925 for (size_t n
= 0; n
< m_text
.Len(); n
++)
927 if (m_text
[n
] != wxT('\\'))
931 gtk_label_set_text_with_mnemonic( GTK_LABEL(label
), wxGTK_CONV(tmp
) );
934 gtk_label_set( label
, wxGTK_CONV( m_text
) );
937 (void)gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV(m_text
) );
938 gtk_accel_label_refetch( GTK_ACCEL_LABEL(label
) );
943 // it's valid for this function to be called even if m_menuItem == NULL
944 void wxMenuItem::DoSetText( const wxString
& str
)
946 // '\t' is the deliminator indicating a hot key
948 const wxChar
*pc
= str
;
949 while ( (*pc
!= wxT('\0')) && (*pc
!= wxT('\t')) )
951 if ((*pc
== wxT('&')) && (*(pc
+1) == wxT('&')))
953 // "&" is doubled to indicate "&" instead of accelerator
957 else if (*pc
== wxT('&'))
961 #if GTK_CHECK_VERSION(2, 0, 0)
962 else if ( *pc
== wxT('_') ) // escape underscores
964 // m_text << wxT("__"); doesn't work
967 else if (*pc
== wxT('/')) // we have to escape slashes
969 m_text
<< wxT("\\/");
971 else if (*pc
== wxT('\\')) // we have to double backslashes
973 m_text
<< wxT("\\\\");
976 else if ( *pc
== wxT('_') ) // escape underscores
980 else if (*pc
== wxT('/')) /* we have to filter out slashes ... */
982 m_text
<< wxT('\\'); /* ... and replace them with back slashes */
991 // wxPrintf( L"str %s m_text %s\n", str.c_str(), m_text.c_str() );
1004 wxAcceleratorEntry
*wxMenuItem::GetAccel() const
1009 return (wxAcceleratorEntry
*)NULL
;
1012 // as wxGetAccelFromString() looks for TAB, insert a dummy one here
1014 label
<< wxT('\t') << GetHotKey();
1016 return wxGetAccelFromString(label
);
1019 #endif // wxUSE_ACCEL
1021 void wxMenuItem::Check( bool check
)
1023 wxCHECK_RET( m_menuItem
, wxT("invalid menu item") );
1025 if (check
== m_isChecked
)
1028 wxMenuItemBase::Check( check
);
1030 switch ( GetKind() )
1034 gtk_check_menu_item_set_state( (GtkCheckMenuItem
*)m_menuItem
, (gint
)check
);
1038 wxFAIL_MSG( _T("can't check this item") );
1042 void wxMenuItem::Enable( bool enable
)
1044 wxCHECK_RET( m_menuItem
, wxT("invalid menu item") );
1046 gtk_widget_set_sensitive( m_menuItem
, enable
);
1047 wxMenuItemBase::Enable( enable
);
1050 bool wxMenuItem::IsChecked() const
1052 wxCHECK_MSG( m_menuItem
, FALSE
, wxT("invalid menu item") );
1054 wxCHECK_MSG( IsCheckable(), FALSE
,
1055 wxT("can't get state of uncheckable item!") );
1057 return ((GtkCheckMenuItem
*)m_menuItem
)->active
!= 0;
1060 wxString
wxMenuItem::GetFactoryPath() const
1062 // In order to get the pointer to the item we need the item
1063 // text _without_ underscores in GTK 1.2
1064 wxString
path( wxT("<main>/") );
1066 for ( const wxChar
*pc
= m_text
.c_str(); *pc
; pc
++ )
1068 if ( *pc
== wxT('_') )
1073 // remove '_' unconditionally
1078 // don't remove ampersands '&' since if we have them in the menu item title
1079 // it means that they were doubled to indicate "&" instead of accelerator
1087 //-----------------------------------------------------------------------------
1089 //-----------------------------------------------------------------------------
1091 IMPLEMENT_DYNAMIC_CLASS(wxMenu
,wxEvtHandler
)
1095 m_accel
= gtk_accel_group_new();
1096 m_factory
= gtk_item_factory_new( GTK_TYPE_MENU
, "<main>", m_accel
);
1097 m_menu
= gtk_item_factory_get_widget( m_factory
, "<main>" );
1099 m_owner
= (GtkWidget
*) NULL
;
1101 // Tearoffs are entries, just like separators. So if we want this
1102 // menu to be a tear-off one, we just append a tearoff entry
1104 if(m_style
& wxMENU_TEAROFF
)
1106 GtkItemFactoryEntry entry
;
1107 entry
.path
= (char *)"/tearoff";
1108 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
1109 entry
.callback_action
= 0;
1110 entry
.item_type
= (char *)"<Tearoff>";
1111 entry
.accelerator
= (gchar
*) NULL
;
1112 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
1113 //GtkWidget *menuItem = gtk_item_factory_get_widget( m_factory, "<main>/tearoff" );
1116 // append the title as the very first entry if we have it
1119 Append(-2, m_title
);
1126 WX_CLEAR_LIST(wxMenuItemList
, m_items
);
1128 if ( GTK_IS_WIDGET( m_menu
))
1129 gtk_widget_destroy( m_menu
);
1131 gtk_object_unref( GTK_OBJECT(m_factory
) );
1134 bool wxMenu::GtkAppend(wxMenuItem
*mitem
)
1136 GtkWidget
*menuItem
;
1138 #if defined(USE_MENU_BITMAPS) || !GTK_CHECK_VERSION(1, 2, 0)
1139 bool appended
= FALSE
;
1142 // does this item terminate the current radio group?
1143 bool endOfRadioGroup
= TRUE
;
1145 if ( mitem
->IsSeparator() )
1147 GtkItemFactoryEntry entry
;
1148 entry
.path
= (char *)"/sep";
1149 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
1150 entry
.callback_action
= 0;
1151 entry
.item_type
= (char *)"<Separator>";
1152 entry
.accelerator
= (gchar
*) NULL
;
1154 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
1156 // this will be wrong for more than one separator. do we care?
1157 menuItem
= gtk_item_factory_get_widget( m_factory
, "<main>/sep" );
1159 // we might have a separator inside a radio group
1160 endOfRadioGroup
= FALSE
;
1162 else if ( mitem
->IsSubMenu() )
1164 // text has "_" instead of "&" after mitem->SetText()
1165 wxString
text( mitem
->GetText() );
1167 // local buffer in multibyte form
1170 strcat( buf
, wxGTK_CONV( text
) );
1172 GtkItemFactoryEntry entry
;
1174 entry
.callback
= (GtkItemFactoryCallback
) 0;
1175 entry
.callback_action
= 0;
1176 entry
.item_type
= (char *)"<Branch>";
1177 entry
.accelerator
= (gchar
*) NULL
;
1179 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
1181 wxString
path( mitem
->GetFactoryPath() );
1182 menuItem
= gtk_item_factory_get_item( m_factory
, wxGTK_CONV( path
) );
1184 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menuItem
), mitem
->GetSubMenu()->m_menu
);
1186 // if adding a submenu to a menu already existing in the menu bar, we
1187 // must set invoking window to allow processing events from this
1189 if ( m_invokingWindow
)
1190 wxMenubarSetInvokingWindow(mitem
->GetSubMenu(), m_invokingWindow
);
1192 #ifdef USE_MENU_BITMAPS
1193 else if (mitem
->GetBitmap().Ok()) // An item with bitmap
1195 wxString
text( mitem
->GetText() );
1196 const wxBitmap
*bitmap
= &mitem
->GetBitmap();
1198 menuItem
= gtk_pixmap_menu_item_new ();
1199 GtkWidget
*label
= gtk_accel_label_new ( wxGTK_CONV( text
) );
1200 gtk_misc_set_alignment (GTK_MISC (label
), 0.0, 0.5);
1201 gtk_container_add (GTK_CONTAINER (menuItem
), label
);
1202 gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (label
), menuItem
);
1204 GdkModifierType accel_mods
;
1206 // accelerator for the item, as specified by its label
1207 // (ex. Ctrl+O for open)
1208 gtk_accelerator_parse(GetHotKey(*mitem
).c_str(),
1209 &accel_key
, &accel_mods
);
1210 if (accel_key
!= GDK_VoidSymbol
)
1212 gtk_widget_add_accelerator (menuItem
,
1214 gtk_menu_get_accel_group(
1216 accel_key
, accel_mods
,
1220 // accelerator for the underlined char (ex ALT+F for the File menu)
1221 accel_key
= gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV( text
) );
1222 if (accel_key
!= GDK_VoidSymbol
)
1224 gtk_widget_add_accelerator (menuItem
,
1226 gtk_menu_ensure_uline_accel_group (
1232 gtk_widget_show (label
);
1234 mitem
->SetLabelWidget(label
);
1236 GtkWidget
* pixmap
= gtk_pixmap_new( bitmap
->GetPixmap(), bitmap
->GetMask() ? bitmap
->GetMask()->GetBitmap() : (GdkBitmap
* )NULL
);
1237 gtk_widget_show(pixmap
);
1238 gtk_pixmap_menu_item_set_pixmap(GTK_PIXMAP_MENU_ITEM( menuItem
), pixmap
);
1240 gtk_signal_connect( GTK_OBJECT(menuItem
), "activate",
1241 GTK_SIGNAL_FUNC(gtk_menu_clicked_callback
),
1244 gtk_menu_append( GTK_MENU(m_menu
), menuItem
);
1246 gtk_widget_show( menuItem
);
1248 appended
= TRUE
; // We've done this, don't do it again
1250 #endif // USE_MENU_BITMAPS
1251 else // a normal item
1253 // text has "_" instead of "&" after mitem->SetText() so don't use it
1254 wxString
text( mitem
->GetText() );
1256 // buffers containing the menu item path and type in multibyte form
1260 strcpy( bufPath
, "/" );
1261 strncat( bufPath
, wxGTK_CONV(text
), WXSIZEOF(bufPath
) - 2 );
1262 bufPath
[WXSIZEOF(bufPath
) - 1] = '\0';
1264 GtkItemFactoryEntry entry
;
1265 entry
.path
= bufPath
;
1266 entry
.callback
= (GtkItemFactoryCallback
) gtk_menu_clicked_callback
;
1267 entry
.callback_action
= 0;
1270 const char *item_type
;
1271 switch ( mitem
->GetKind() )
1274 item_type
= "<CheckItem>";
1278 if ( m_pathLastRadio
.empty() )
1280 // start of a new radio group
1281 item_type
= "<RadioItem>";
1282 wxString
tmp( wxGTK_CONV_BACK( bufPath
) );
1284 m_pathLastRadio
= tmp
;
1286 else // continue the radio group
1288 pathRadio
= m_pathLastRadio
;
1289 pathRadio
.Replace(wxT("_"), wxT(""));
1290 pathRadio
.Prepend(wxT("<main>/"));
1292 strncpy(bufType
, wxGTK_CONV(pathRadio
), WXSIZEOF(bufType
));
1293 bufType
[WXSIZEOF(bufType
) - 1] = '\0';
1294 item_type
= bufType
;
1297 // continue the existing radio group, if any
1298 endOfRadioGroup
= FALSE
;
1302 wxFAIL_MSG( _T("unexpected menu item kind") );
1306 item_type
= "<Item>";
1310 entry
.item_type
= (char *)item_type
; // cast needed for GTK+
1311 entry
.accelerator
= (gchar
*) NULL
;
1314 // due to an apparent bug in GTK+, we have to use a static buffer here -
1315 // otherwise GTK+ 1.2.2 manages to override the memory we pass to it
1317 char s_accel
[50]; // should be big enough, we check for overruns
1318 wxString
tmp( GetHotKey(*mitem
) );
1319 strncpy(s_accel
, wxGTK_CONV( tmp
), WXSIZEOF(s_accel
));
1320 s_accel
[WXSIZEOF(s_accel
) - 1] = '\0';
1321 entry
.accelerator
= s_accel
;
1322 #else // !wxUSE_ACCEL
1323 entry
.accelerator
= (char*) NULL
;
1324 #endif // wxUSE_ACCEL/!wxUSE_ACCEL
1326 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); /* what is 2 ? */
1328 wxString
path( mitem
->GetFactoryPath() );
1329 menuItem
= gtk_item_factory_get_widget( m_factory
, wxGTK_CONV( path
) );
1332 wxLogError( wxT("Wrong menu path: %s\n"), path
.c_str() );
1335 if ( !mitem
->IsSeparator() )
1337 wxASSERT_MSG( menuItem
, wxT("invalid menuitem") );
1339 gtk_signal_connect( GTK_OBJECT(menuItem
), "select",
1340 GTK_SIGNAL_FUNC(gtk_menu_hilight_callback
),
1343 gtk_signal_connect( GTK_OBJECT(menuItem
), "deselect",
1344 GTK_SIGNAL_FUNC(gtk_menu_nolight_callback
),
1348 mitem
->SetMenuItem(menuItem
);
1350 if ( endOfRadioGroup
)
1352 m_pathLastRadio
.clear();
1358 bool wxMenu::DoAppend(wxMenuItem
*mitem
)
1360 return GtkAppend(mitem
) && wxMenuBase::DoAppend(mitem
);
1363 bool wxMenu::DoInsert(size_t pos
, wxMenuItem
*item
)
1365 if ( !wxMenuBase::DoInsert(pos
, item
) )
1368 // GTK+ doesn't have a function to insert a menu using GtkItemFactory (as
1369 // of version 1.2.6), so we first append the item and then change its
1371 if ( !GtkAppend(item
) )
1374 if ( m_style
& wxMENU_TEAROFF
)
1376 // change the position as the first item is the tear-off marker
1380 GtkMenuShell
*menu_shell
= GTK_MENU_SHELL(m_factory
->widget
);
1381 gpointer data
= g_list_last(menu_shell
->children
)->data
;
1382 menu_shell
->children
= g_list_remove(menu_shell
->children
, data
);
1383 menu_shell
->children
= g_list_insert(menu_shell
->children
, data
, pos
);
1388 wxMenuItem
*wxMenu::DoRemove(wxMenuItem
*item
)
1390 if ( !wxMenuBase::DoRemove(item
) )
1391 return (wxMenuItem
*)NULL
;
1393 // TODO: this code doesn't delete the item factory item and this seems
1394 // impossible as of GTK 1.2.6.
1395 gtk_widget_destroy( item
->GetMenuItem() );
1400 int wxMenu::FindMenuIdByMenuItem( GtkWidget
*menuItem
) const
1402 wxMenuItemList::compatibility_iterator node
= m_items
.GetFirst();
1405 wxMenuItem
*item
= node
->GetData();
1406 if (item
->GetMenuItem() == menuItem
)
1407 return item
->GetId();
1408 node
= node
->GetNext();
1414 // ----------------------------------------------------------------------------
1416 // ----------------------------------------------------------------------------
1418 #if GTK_CHECK_VERSION(1, 2, 0) && wxUSE_ACCEL
1420 static wxString
GetHotKey( const wxMenuItem
& item
)
1424 wxAcceleratorEntry
*accel
= item
.GetAccel();
1427 int flags
= accel
->GetFlags();
1428 if ( flags
& wxACCEL_ALT
)
1429 hotkey
+= wxT("<alt>");
1430 if ( flags
& wxACCEL_CTRL
)
1431 hotkey
+= wxT("<control>");
1432 if ( flags
& wxACCEL_SHIFT
)
1433 hotkey
+= wxT("<shift>");
1435 int code
= accel
->GetKeyCode();
1450 hotkey
<< wxT('F') << code
- WXK_F1
+ 1;
1453 // TODO: we should use gdk_keyval_name() (a.k.a.
1454 // XKeysymToString) here as well as hardcoding the keysym
1455 // names this might be not portable
1456 case WXK_NUMPAD_INSERT
:
1457 hotkey
<< wxT("KP_Insert" );
1459 case WXK_NUMPAD_DELETE
:
1460 hotkey
<< wxT("KP_Delete" );
1463 hotkey
<< wxT("Insert" );
1466 hotkey
<< wxT("Delete" );
1469 hotkey
<< wxT("Up" );
1472 hotkey
<< wxT("Down" );
1475 hotkey
<< wxT("Prior" );
1478 hotkey
<< wxT("Next" );
1481 hotkey
<< wxT("Left" );
1484 hotkey
<< wxT("Right" );
1487 hotkey
<< wxT("Home" );
1490 hotkey
<< wxT("End" );
1493 hotkey
<< wxT("Return" );
1496 // if there are any other keys wxGetAccelFromString() may
1497 // return, we should process them here
1502 wxString name
= wxGTK_CONV_BACK( gdk_keyval_name((guint
)code
) );
1510 wxFAIL_MSG( wxT("unknown keyboard accel") );
1519 #endif // wxUSE_ACCEL
1522 //-----------------------------------------------------------------------------
1523 // substitute for missing GtkPixmapMenuItem
1524 //-----------------------------------------------------------------------------
1526 #ifdef USE_MENU_BITMAPS
1529 * Copyright (C) 1998, 1999, 2000 Free Software Foundation
1530 * All rights reserved.
1532 * This file is part of the Gnome Library.
1534 * The Gnome Library is free software; you can redistribute it and/or
1535 * modify it under the terms of the GNU Library General Public License as
1536 * published by the Free Software Foundation; either version 2 of the
1537 * License, or (at your option) any later version.
1539 * The Gnome Library is distributed in the hope that it will be useful,
1540 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1541 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1542 * Library General Public License for more details.
1544 * You should have received a copy of the GNU Library General Public
1545 * License along with the Gnome Library; see the file COPYING.LIB. If not,
1546 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
1547 * Boston, MA 02111-1307, USA.
1553 /* Author: Dietmar Maurer <dm@vlsivie.tuwien.ac.at> */
1555 #include <gtk/gtkaccellabel.h>
1556 #include <gtk/gtksignal.h>
1557 #include <gtk/gtkmenuitem.h>
1558 #include <gtk/gtkmenu.h>
1559 #include <gtk/gtkcontainer.h>
1564 static void gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass
*klass
);
1565 static void gtk_pixmap_menu_item_init (GtkPixmapMenuItem
*menu_item
);
1566 static void gtk_pixmap_menu_item_draw (GtkWidget
*widget
,
1567 GdkRectangle
*area
);
1568 static gint
gtk_pixmap_menu_item_expose (GtkWidget
*widget
,
1569 GdkEventExpose
*event
);
1571 /* we must override the following functions */
1573 static void gtk_pixmap_menu_item_map (GtkWidget
*widget
);
1574 static void gtk_pixmap_menu_item_size_allocate (GtkWidget
*widget
,
1575 GtkAllocation
*allocation
);
1576 static void gtk_pixmap_menu_item_forall (GtkContainer
*container
,
1577 gboolean include_internals
,
1578 GtkCallback callback
,
1579 gpointer callback_data
);
1580 static void gtk_pixmap_menu_item_size_request (GtkWidget
*widget
,
1581 GtkRequisition
*requisition
);
1582 static void gtk_pixmap_menu_item_remove (GtkContainer
*container
,
1585 static void changed_have_pixmap_status (GtkPixmapMenuItem
*menu_item
);
1587 static GtkMenuItemClass
*parent_class
= NULL
;
1591 #define BORDER_SPACING 3
1592 #define PMAP_WIDTH 20
1595 gtk_pixmap_menu_item_get_type (void)
1597 static GtkType pixmap_menu_item_type
= 0;
1599 if (!pixmap_menu_item_type
)
1601 GtkTypeInfo pixmap_menu_item_info
=
1603 (char *)"GtkPixmapMenuItem",
1604 sizeof (GtkPixmapMenuItem
),
1605 sizeof (GtkPixmapMenuItemClass
),
1606 (GtkClassInitFunc
) gtk_pixmap_menu_item_class_init
,
1607 (GtkObjectInitFunc
) gtk_pixmap_menu_item_init
,
1608 /* reserved_1 */ NULL
,
1609 /* reserved_2 */ NULL
,
1610 (GtkClassInitFunc
) NULL
,
1613 pixmap_menu_item_type
= gtk_type_unique (gtk_menu_item_get_type (),
1614 &pixmap_menu_item_info
);
1617 return pixmap_menu_item_type
;
1621 * gtk_pixmap_menu_item_new
1623 * Creates a new pixmap menu item. Use gtk_pixmap_menu_item_set_pixmap()
1624 * to set the pixmap wich is displayed at the left side.
1627 * &GtkWidget pointer to new menu item
1631 gtk_pixmap_menu_item_new (void)
1633 return GTK_WIDGET (gtk_type_new (gtk_pixmap_menu_item_get_type ()));
1637 gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass
*klass
)
1639 GtkObjectClass
*object_class
;
1640 GtkWidgetClass
*widget_class
;
1641 GtkMenuItemClass
*menu_item_class
;
1642 GtkContainerClass
*container_class
;
1644 object_class
= (GtkObjectClass
*) klass
;
1645 widget_class
= (GtkWidgetClass
*) klass
;
1646 menu_item_class
= (GtkMenuItemClass
*) klass
;
1647 container_class
= (GtkContainerClass
*) klass
;
1649 parent_class
= (GtkMenuItemClass
*) gtk_type_class (gtk_menu_item_get_type ());
1651 widget_class
->draw
= gtk_pixmap_menu_item_draw
;
1652 widget_class
->expose_event
= gtk_pixmap_menu_item_expose
;
1653 widget_class
->map
= gtk_pixmap_menu_item_map
;
1654 widget_class
->size_allocate
= gtk_pixmap_menu_item_size_allocate
;
1655 widget_class
->size_request
= gtk_pixmap_menu_item_size_request
;
1657 container_class
->forall
= gtk_pixmap_menu_item_forall
;
1658 container_class
->remove
= gtk_pixmap_menu_item_remove
;
1660 klass
->orig_toggle_size
= menu_item_class
->toggle_size
;
1661 klass
->have_pixmap_count
= 0;
1665 gtk_pixmap_menu_item_init (GtkPixmapMenuItem
*menu_item
)
1669 mi
= GTK_MENU_ITEM (menu_item
);
1671 menu_item
->pixmap
= NULL
;
1675 gtk_pixmap_menu_item_draw (GtkWidget
*widget
,
1678 g_return_if_fail (widget
!= NULL
);
1679 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
));
1680 g_return_if_fail (area
!= NULL
);
1682 if (GTK_WIDGET_CLASS (parent_class
)->draw
)
1683 (* GTK_WIDGET_CLASS (parent_class
)->draw
) (widget
, area
);
1685 if (GTK_WIDGET_DRAWABLE (widget
) &&
1686 GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
) {
1687 gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
),NULL
);
1692 gtk_pixmap_menu_item_expose (GtkWidget
*widget
,
1693 GdkEventExpose
*event
)
1695 g_return_val_if_fail (widget
!= NULL
, FALSE
);
1696 g_return_val_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
), FALSE
);
1697 g_return_val_if_fail (event
!= NULL
, FALSE
);
1699 if (GTK_WIDGET_CLASS (parent_class
)->expose_event
)
1700 (* GTK_WIDGET_CLASS (parent_class
)->expose_event
) (widget
, event
);
1702 if (GTK_WIDGET_DRAWABLE (widget
) &&
1703 GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
) {
1704 gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
),NULL
);
1711 * gtk_pixmap_menu_item_set_pixmap
1712 * @menu_item: Pointer to the pixmap menu item
1713 * @pixmap: Pointer to a pixmap widget
1715 * Set the pixmap of the menu item.
1720 gtk_pixmap_menu_item_set_pixmap (GtkPixmapMenuItem
*menu_item
,
1723 g_return_if_fail (menu_item
!= NULL
);
1724 g_return_if_fail (pixmap
!= NULL
);
1725 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (menu_item
));
1726 g_return_if_fail (GTK_IS_WIDGET (pixmap
));
1727 g_return_if_fail (menu_item
->pixmap
== NULL
);
1729 gtk_widget_set_parent (pixmap
, GTK_WIDGET (menu_item
));
1730 menu_item
->pixmap
= pixmap
;
1732 if (GTK_WIDGET_REALIZED (pixmap
->parent
) &&
1733 !GTK_WIDGET_REALIZED (pixmap
))
1734 gtk_widget_realize (pixmap
);
1736 if (GTK_WIDGET_VISIBLE (pixmap
->parent
)) {
1737 if (GTK_WIDGET_MAPPED (pixmap
->parent
) &&
1738 GTK_WIDGET_VISIBLE(pixmap
) &&
1739 !GTK_WIDGET_MAPPED (pixmap
))
1740 gtk_widget_map (pixmap
);
1743 changed_have_pixmap_status(menu_item
);
1745 if (GTK_WIDGET_VISIBLE (pixmap
) && GTK_WIDGET_VISIBLE (menu_item
))
1746 gtk_widget_queue_resize (pixmap
);
1750 gtk_pixmap_menu_item_map (GtkWidget
*widget
)
1752 GtkPixmapMenuItem
*menu_item
;
1754 g_return_if_fail (widget
!= NULL
);
1755 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
));
1757 menu_item
= GTK_PIXMAP_MENU_ITEM(widget
);
1759 GTK_WIDGET_CLASS(parent_class
)->map(widget
);
1761 if (menu_item
->pixmap
&&
1762 GTK_WIDGET_VISIBLE (menu_item
->pixmap
) &&
1763 !GTK_WIDGET_MAPPED (menu_item
->pixmap
))
1764 gtk_widget_map (menu_item
->pixmap
);
1768 gtk_pixmap_menu_item_size_allocate (GtkWidget
*widget
,
1769 GtkAllocation
*allocation
)
1771 GtkPixmapMenuItem
*pmenu_item
;
1773 pmenu_item
= GTK_PIXMAP_MENU_ITEM(widget
);
1775 if (pmenu_item
->pixmap
&& GTK_WIDGET_VISIBLE(pmenu_item
))
1777 GtkAllocation child_allocation
;
1780 border_width
= GTK_CONTAINER (widget
)->border_width
;
1782 child_allocation
.width
= pmenu_item
->pixmap
->requisition
.width
;
1783 child_allocation
.height
= pmenu_item
->pixmap
->requisition
.height
;
1784 child_allocation
.x
= border_width
+ BORDER_SPACING
;
1785 child_allocation
.y
= (border_width
+ BORDER_SPACING
1786 + (((allocation
->height
- child_allocation
.height
) - child_allocation
.x
)
1787 / 2)); /* center pixmaps vertically */
1788 gtk_widget_size_allocate (pmenu_item
->pixmap
, &child_allocation
);
1791 if (GTK_WIDGET_CLASS (parent_class
)->size_allocate
)
1792 GTK_WIDGET_CLASS(parent_class
)->size_allocate (widget
, allocation
);
1796 gtk_pixmap_menu_item_forall (GtkContainer
*container
,
1797 gboolean include_internals
,
1798 GtkCallback callback
,
1799 gpointer callback_data
)
1801 GtkPixmapMenuItem
*menu_item
;
1803 g_return_if_fail (container
!= NULL
);
1804 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container
));
1805 g_return_if_fail (callback
!= NULL
);
1807 menu_item
= GTK_PIXMAP_MENU_ITEM (container
);
1809 if (menu_item
->pixmap
)
1810 (* callback
) (menu_item
->pixmap
, callback_data
);
1812 GTK_CONTAINER_CLASS(parent_class
)->forall(container
,include_internals
,
1813 callback
,callback_data
);
1817 gtk_pixmap_menu_item_size_request (GtkWidget
*widget
,
1818 GtkRequisition
*requisition
)
1820 GtkPixmapMenuItem
*menu_item
;
1821 GtkRequisition req
= {0, 0};
1823 g_return_if_fail (widget
!= NULL
);
1824 g_return_if_fail (GTK_IS_MENU_ITEM (widget
));
1825 g_return_if_fail (requisition
!= NULL
);
1827 GTK_WIDGET_CLASS(parent_class
)->size_request(widget
,requisition
);
1829 menu_item
= GTK_PIXMAP_MENU_ITEM (widget
);
1831 if (menu_item
->pixmap
)
1832 gtk_widget_size_request(menu_item
->pixmap
, &req
);
1834 requisition
->height
= MAX(req
.height
+ GTK_CONTAINER(widget
)->border_width
+ BORDER_SPACING
, (unsigned int) requisition
->height
);
1835 requisition
->width
+= (req
.width
+ GTK_CONTAINER(widget
)->border_width
+ BORDER_SPACING
);
1839 gtk_pixmap_menu_item_remove (GtkContainer
*container
,
1843 gboolean widget_was_visible
;
1845 g_return_if_fail (container
!= NULL
);
1846 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container
));
1847 g_return_if_fail (child
!= NULL
);
1848 g_return_if_fail (GTK_IS_WIDGET (child
));
1850 bin
= GTK_BIN (container
);
1851 g_return_if_fail ((bin
->child
== child
||
1852 (GTK_PIXMAP_MENU_ITEM(container
)->pixmap
== child
)));
1854 widget_was_visible
= GTK_WIDGET_VISIBLE (child
);
1856 gtk_widget_unparent (child
);
1857 if (bin
->child
== child
)
1860 GTK_PIXMAP_MENU_ITEM(container
)->pixmap
= NULL
;
1861 changed_have_pixmap_status(GTK_PIXMAP_MENU_ITEM(container
));
1864 if (widget_was_visible
)
1865 gtk_widget_queue_resize (GTK_WIDGET (container
));
1869 /* important to only call this if there was actually a _change_ in pixmap == NULL */
1871 changed_have_pixmap_status (GtkPixmapMenuItem
*menu_item
)
1873 if (menu_item
->pixmap
!= NULL
) {
1874 GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
+= 1;
1876 if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
== 1) {
1877 /* Install pixmap toggle size */
1878 GTK_MENU_ITEM_GET_CLASS(menu_item
)->toggle_size
= MAX(GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->orig_toggle_size
, PMAP_WIDTH
);
1881 GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
-= 1;
1883 if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
== 0) {
1884 /* Install normal toggle size */
1885 GTK_MENU_ITEM_GET_CLASS(menu_item
)->toggle_size
= GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->orig_toggle_size
;
1889 /* Note that we actually need to do this for _all_ GtkPixmapMenuItem
1890 whenever the klass->toggle_size changes; but by doing it anytime
1891 this function is called, we get the same effect, just because of
1892 how the preferences option to show pixmaps works. Bogus, broken.
1894 if (GTK_WIDGET_VISIBLE(GTK_WIDGET(menu_item
)))
1895 gtk_widget_queue_resize(GTK_WIDGET(menu_item
));
1898 #endif // USE_MENU_BITMAPS