1 /////////////////////////////////////////////////////////////////////////////
4 // Author: Robert Roebling
6 // Copyright: (c) 1998 Robert Roebling
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
10 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
11 #pragma implementation "menu.h"
12 #pragma implementation "menuitem.h"
15 // For compilers that support precompilation, includes "wx.h".
16 #include "wx/wxprec.h"
21 #include "wx/bitmap.h"
28 #include "wx/gtk/private.h"
30 #include <gdk/gdkkeysyms.h>
32 // FIXME: is this right? somehow I don't think so (VZ)
34 #include <glib-object.h>
36 #define gtk_accel_group_attach(g, o) _gtk_accel_group_attach((g), (o))
37 #define gtk_accel_group_detach(g, o) _gtk_accel_group_detach((g), (o))
38 #define gtk_menu_ensure_uline_accel_group(m) gtk_menu_get_accel_group(m)
40 #define ACCEL_OBJECT GObject
41 #define ACCEL_OBJECTS(a) (a)->acceleratables
42 #define ACCEL_OBJ_CAST(obj) G_OBJECT(obj)
44 #define ACCEL_OBJECT GtkObject
45 #define ACCEL_OBJECTS(a) (a)->attach_objects
46 #define ACCEL_OBJ_CAST(obj) GTK_OBJECT(obj)
49 //-----------------------------------------------------------------------------
51 //-----------------------------------------------------------------------------
53 extern void wxapp_install_idle_handler();
56 #if GTK_CHECK_VERSION(1, 2, 0) && wxUSE_ACCEL
57 static wxString
GetHotKey( const wxMenuItem
& item
);
60 //-----------------------------------------------------------------------------
61 // substitute for missing GtkPixmapMenuItem
62 //-----------------------------------------------------------------------------
64 // FIXME: I can't make this compile with GTK+ 2.0, disabling for now (VZ)
66 #define USE_MENU_BITMAPS
69 #ifdef USE_MENU_BITMAPS
71 #define GTK_TYPE_PIXMAP_MENU_ITEM (gtk_pixmap_menu_item_get_type ())
72 #define GTK_PIXMAP_MENU_ITEM(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_PIXMAP_MENU_ITEM, GtkPixmapMenuItem))
73 #define GTK_PIXMAP_MENU_ITEM_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_PIXMAP_MENU_ITEM, GtkPixmapMenuItemClass))
74 #define GTK_IS_PIXMAP_MENU_ITEM(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_PIXMAP_MENU_ITEM))
75 #define GTK_IS_PIXMAP_MENU_ITEM_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PIXMAP_MENU_ITEM))
76 //#define GTK_PIXMAP_MENU_ITEM_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_PIXMAP_MENU_ITEM))
77 #define GTK_PIXMAP_MENU_ITEM_GET_CLASS(obj) (GTK_PIXMAP_MENU_ITEM_CLASS( GTK_OBJECT_GET_CLASS(obj)))
79 #ifndef GTK_MENU_ITEM_GET_CLASS
80 #define GTK_MENU_ITEM_GET_CLASS(obj) (GTK_MENU_ITEM_CLASS( GTK_OBJECT_GET_CLASS(obj)))
83 typedef struct _GtkPixmapMenuItem GtkPixmapMenuItem
;
84 typedef struct _GtkPixmapMenuItemClass GtkPixmapMenuItemClass
;
86 struct _GtkPixmapMenuItem
88 GtkMenuItem menu_item
;
93 struct _GtkPixmapMenuItemClass
95 GtkMenuItemClass parent_class
;
97 guint orig_toggle_size
;
98 guint have_pixmap_count
;
102 GtkType
gtk_pixmap_menu_item_get_type (void);
103 GtkWidget
* gtk_pixmap_menu_item_new (void);
104 void gtk_pixmap_menu_item_set_pixmap (GtkPixmapMenuItem
*menu_item
,
107 #endif // USE_MENU_BITMAPS
109 //-----------------------------------------------------------------------------
111 //-----------------------------------------------------------------------------
113 static wxString
wxReplaceUnderscore( const wxString
& title
)
117 /* GTK 1.2 wants to have "_" instead of "&" for accelerators */
120 while (*pc
!= wxT('\0'))
122 if ((*pc
== wxT('&')) && (*(pc
+1) == wxT('&')))
124 // "&" is doubled to indicate "&" instead of accelerator
128 else if (*pc
== wxT('&'))
130 #if GTK_CHECK_VERSION(1, 2, 0)
134 #if GTK_CHECK_VERSION(2, 0, 0)
135 else if (*pc
== wxT('/'))
139 else if (*pc
== wxT('\\'))
143 #elif GTK_CHECK_VERSION(1, 2, 0)
144 else if (*pc
== wxT('/'))
152 if ( *pc
== wxT('_') )
154 // underscores must be doubled to prevent them from being
155 // interpreted as accelerator character prefix by GTK
167 //-----------------------------------------------------------------------------
168 // activate message from GTK
169 //-----------------------------------------------------------------------------
171 static void gtk_menu_open_callback( GtkWidget
*widget
, wxMenu
*menu
)
173 if (g_isIdle
) wxapp_install_idle_handler();
175 wxMenuEvent
event( wxEVT_MENU_OPEN
, -1, menu
);
176 event
.SetEventObject( menu
);
178 wxEvtHandler
* handler
= menu
->GetEventHandler();
179 if (handler
&& handler
->ProcessEvent(event
))
182 wxWindow
*win
= menu
->GetInvokingWindow();
183 if (win
) win
->GetEventHandler()->ProcessEvent( event
);
186 //-----------------------------------------------------------------------------
188 //-----------------------------------------------------------------------------
190 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar
,wxWindow
)
192 wxMenuBar::wxMenuBar( long style
)
194 /* the parent window is known after wxFrame::SetMenu() */
195 m_needParent
= FALSE
;
197 m_invokingWindow
= (wxWindow
*) NULL
;
199 if (!PreCreation( (wxWindow
*) NULL
, wxDefaultPosition
, wxDefaultSize
) ||
200 !CreateBase( (wxWindow
*) NULL
, -1, wxDefaultPosition
, wxDefaultSize
, style
, wxDefaultValidator
, wxT("menubar") ))
202 wxFAIL_MSG( wxT("wxMenuBar creation failed") );
206 /* GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has. */
207 #if GTK_CHECK_VERSION(1, 2, 1)
208 m_accel
= gtk_accel_group_new();
209 m_factory
= gtk_item_factory_new( GTK_TYPE_MENU_BAR
, "<main>", m_accel
);
210 m_menubar
= gtk_item_factory_get_widget( m_factory
, "<main>" );
212 m_menubar
= gtk_menu_bar_new();
215 if (style
& wxMB_DOCKABLE
)
217 m_widget
= gtk_handle_box_new();
218 gtk_container_add( GTK_CONTAINER(m_widget
), GTK_WIDGET(m_menubar
) );
219 gtk_widget_show( GTK_WIDGET(m_menubar
) );
223 m_widget
= GTK_WIDGET(m_menubar
);
231 wxMenuBar::wxMenuBar()
233 /* the parent window is known after wxFrame::SetMenu() */
234 m_needParent
= FALSE
;
236 m_invokingWindow
= (wxWindow
*) NULL
;
238 if (!PreCreation( (wxWindow
*) NULL
, wxDefaultPosition
, wxDefaultSize
) ||
239 !CreateBase( (wxWindow
*) NULL
, -1, wxDefaultPosition
, wxDefaultSize
, 0, wxDefaultValidator
, wxT("menubar") ))
241 wxFAIL_MSG( wxT("wxMenuBar creation failed") );
245 /* GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has. */
246 #if GTK_CHECK_VERSION(1, 2, 1)
247 m_accel
= gtk_accel_group_new();
248 m_factory
= gtk_item_factory_new( GTK_TYPE_MENU_BAR
, "<main>", m_accel
);
249 m_menubar
= gtk_item_factory_get_widget( m_factory
, "<main>" );
251 m_menubar
= gtk_menu_bar_new();
254 m_widget
= GTK_WIDGET(m_menubar
);
261 wxMenuBar::~wxMenuBar()
263 // gtk_object_unref( GTK_OBJECT(m_factory) ); why not ?
266 static void wxMenubarUnsetInvokingWindow( wxMenu
*menu
, wxWindow
*win
)
268 menu
->SetInvokingWindow( (wxWindow
*) NULL
);
270 wxWindow
*top_frame
= win
;
271 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
272 top_frame
= top_frame
->GetParent();
274 /* support for native hot keys */
275 gtk_accel_group_detach( menu
->m_accel
, ACCEL_OBJ_CAST(top_frame
->m_widget
) );
277 wxMenuItemList::compatibility_iterator node
= menu
->GetMenuItems().GetFirst();
280 wxMenuItem
*menuitem
= node
->GetData();
281 if (menuitem
->IsSubMenu())
282 wxMenubarUnsetInvokingWindow( menuitem
->GetSubMenu(), win
);
283 node
= node
->GetNext();
287 static void wxMenubarSetInvokingWindow( wxMenu
*menu
, wxWindow
*win
)
289 menu
->SetInvokingWindow( win
);
291 #if GTK_CHECK_VERSION(1, 2, 1)
292 wxWindow
*top_frame
= win
;
293 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
294 top_frame
= top_frame
->GetParent();
296 /* support for native hot keys */
297 ACCEL_OBJECT
*obj
= ACCEL_OBJ_CAST(top_frame
->m_widget
);
298 if ( !g_slist_find( ACCEL_OBJECTS(menu
->m_accel
), obj
) )
299 gtk_accel_group_attach( menu
->m_accel
, obj
);
300 #endif // GTK+ 1.2.1+
302 wxMenuItemList::compatibility_iterator node
= menu
->GetMenuItems().GetFirst();
305 wxMenuItem
*menuitem
= node
->GetData();
306 if (menuitem
->IsSubMenu())
307 wxMenubarSetInvokingWindow( menuitem
->GetSubMenu(), win
);
308 node
= node
->GetNext();
312 void wxMenuBar::SetInvokingWindow( wxWindow
*win
)
314 m_invokingWindow
= win
;
315 #if GTK_CHECK_VERSION(1, 2, 1)
316 wxWindow
*top_frame
= win
;
317 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
318 top_frame
= top_frame
->GetParent();
320 /* support for native key accelerators indicated by underscroes */
321 ACCEL_OBJECT
*obj
= ACCEL_OBJ_CAST(top_frame
->m_widget
);
322 if ( !g_slist_find( ACCEL_OBJECTS(m_accel
), obj
) )
323 gtk_accel_group_attach( m_accel
, obj
);
324 #endif // GTK+ 1.2.1+
326 wxMenuList::compatibility_iterator node
= m_menus
.GetFirst();
329 wxMenu
*menu
= node
->GetData();
330 wxMenubarSetInvokingWindow( menu
, win
);
331 node
= node
->GetNext();
335 void wxMenuBar::UnsetInvokingWindow( wxWindow
*win
)
337 m_invokingWindow
= (wxWindow
*) NULL
;
338 #if GTK_CHECK_VERSION(1, 2, 1)
339 wxWindow
*top_frame
= win
;
340 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
341 top_frame
= top_frame
->GetParent();
343 // support for native key accelerators indicated by underscroes
344 gtk_accel_group_detach( m_accel
, ACCEL_OBJ_CAST(top_frame
->m_widget
) );
345 #endif // GTK+ 1.2.1+
347 wxMenuList::compatibility_iterator node
= m_menus
.GetFirst();
350 wxMenu
*menu
= node
->GetData();
351 wxMenubarUnsetInvokingWindow( menu
, win
);
352 node
= node
->GetNext();
356 bool wxMenuBar::Append( wxMenu
*menu
, const wxString
&title
)
358 if ( !wxMenuBarBase::Append( menu
, title
) )
361 return GtkAppend(menu
, title
);
364 bool wxMenuBar::GtkAppend(wxMenu
*menu
, const wxString
& title
)
366 wxString
str( wxReplaceUnderscore( title
) );
368 // This doesn't have much effect right now.
369 menu
->SetTitle( str
);
371 // GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has.
372 #if GTK_CHECK_VERSION(1, 2, 1)
375 buf
<< wxT('/') << str
.c_str();
377 // local buffer in multibyte form
379 strcpy(cbuf
, wxGTK_CONV(buf
) );
381 GtkItemFactoryEntry entry
;
382 entry
.path
= (gchar
*)cbuf
; // const_cast
383 entry
.accelerator
= (gchar
*) NULL
;
384 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
385 entry
.callback_action
= 0;
386 entry
.item_type
= (char *)"<Branch>";
388 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
389 // in order to get the pointer to the item we need the item text _without_ underscores
390 wxString tmp
= wxT("<main>/");
392 for ( pc
= str
; *pc
!= wxT('\0'); pc
++ )
394 // contrary to the common sense, we must throw out _all_ underscores,
395 // (i.e. "Hello__World" => "HelloWorld" and not "Hello_World" as we
396 // might naively think). IMHO it's a bug in GTK+ (VZ)
397 while (*pc
== wxT('_'))
401 menu
->m_owner
= gtk_item_factory_get_item( m_factory
, wxGTK_CONV( tmp
) );
402 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu
->m_owner
), menu
->m_menu
);
405 menu
->m_owner
= gtk_menu_item_new_with_label( wxGTK_CONV( str
) );
406 gtk_widget_show( menu
->m_owner
);
407 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu
->m_owner
), menu
->m_menu
);
409 gtk_menu_bar_append( GTK_MENU_BAR(m_menubar
), menu
->m_owner
);
413 gtk_signal_connect( GTK_OBJECT(menu
->m_owner
), "activate",
414 GTK_SIGNAL_FUNC(gtk_menu_open_callback
),
417 // m_invokingWindow is set after wxFrame::SetMenuBar(). This call enables
418 // addings menu later on.
419 if (m_invokingWindow
)
421 wxMenubarSetInvokingWindow( menu
, m_invokingWindow
);
423 // OPTIMISE ME: we should probably cache this, or pass it
424 // directly, but for now this is a minimal
425 // change to validate the new dynamic sizing.
426 // see (and refactor :) similar code in Remove
429 wxFrame
*frame
= wxDynamicCast( m_invokingWindow
, wxFrame
);
432 frame
->UpdateMenuBarSize();
438 bool wxMenuBar::Insert(size_t pos
, wxMenu
*menu
, const wxString
& title
)
440 if ( !wxMenuBarBase::Insert(pos
, menu
, title
) )
443 // GTK+ doesn't have a function to insert a menu using GtkItemFactory (as
444 // of version 1.2.6), so we first append the item and then change its
446 if ( !GtkAppend(menu
, title
) )
449 if (pos
+1 >= m_menus
.GetCount())
452 GtkMenuShell
*menu_shell
= GTK_MENU_SHELL(m_factory
->widget
);
453 gpointer data
= g_list_last(menu_shell
->children
)->data
;
454 menu_shell
->children
= g_list_remove(menu_shell
->children
, data
);
455 menu_shell
->children
= g_list_insert(menu_shell
->children
, data
, pos
);
460 wxMenu
*wxMenuBar::Replace(size_t pos
, wxMenu
*menu
, const wxString
& title
)
462 // remove the old item and insert a new one
463 wxMenu
*menuOld
= Remove(pos
);
464 if ( menuOld
&& !Insert(pos
, menu
, title
) )
466 return (wxMenu
*) NULL
;
469 // either Insert() succeeded or Remove() failed and menuOld is NULL
473 static wxMenu
*CopyMenu (wxMenu
*menu
)
475 wxMenu
*menucopy
= new wxMenu ();
476 wxMenuItemList::compatibility_iterator node
= menu
->GetMenuItems().GetFirst();
479 wxMenuItem
*item
= node
->GetData();
480 int itemid
= item
->GetId();
481 wxString text
= item
->GetText();
482 text
.Replace(wxT("_"), wxT("&"));
483 wxMenu
*submenu
= item
->GetSubMenu();
486 wxMenuItem
* itemcopy
= new wxMenuItem(menucopy
,
488 menu
->GetHelpString(itemid
));
489 itemcopy
->SetBitmap(item
->GetBitmap());
490 itemcopy
->SetCheckable(item
->IsCheckable());
491 menucopy
->Append(itemcopy
);
494 menucopy
->Append (itemid
, text
, CopyMenu(submenu
),
495 menu
->GetHelpString(itemid
));
497 node
= node
->GetNext();
503 wxMenu
*wxMenuBar::Remove(size_t pos
)
505 wxMenu
*menu
= wxMenuBarBase::Remove(pos
);
507 return (wxMenu
*) NULL
;
510 GtkMenuShell *menu_shell = GTK_MENU_SHELL(m_factory->widget);
512 printf( "factory entries before %d\n", (int)g_slist_length(m_factory->items) );
513 printf( "menu shell entries before %d\n", (int)g_list_length( menu_shell->children ) );
516 wxMenu
*menucopy
= CopyMenu( menu
);
518 // unparent calls unref() and that would delete the widget so we raise
519 // the ref count to 2 artificially before invoking unparent.
520 gtk_widget_ref( menu
->m_menu
);
521 gtk_widget_unparent( menu
->m_menu
);
523 gtk_widget_destroy( menu
->m_owner
);
528 printf( "factory entries after %d\n", (int)g_slist_length(m_factory->items) );
529 printf( "menu shell entries after %d\n", (int)g_list_length( menu_shell->children ) );
532 if (m_invokingWindow
)
534 // OPTIMISE ME: see comment in GtkAppend
536 wxFrame
*frame
= wxDynamicCast( m_invokingWindow
, wxFrame
);
539 frame
->UpdateMenuBarSize();
545 static int FindMenuItemRecursive( const wxMenu
*menu
, const wxString
&menuString
, const wxString
&itemString
)
547 if (wxMenuItem::GetLabelFromText(menu
->GetTitle()) == wxMenuItem::GetLabelFromText(menuString
))
549 int res
= menu
->FindItem( itemString
);
550 if (res
!= wxNOT_FOUND
)
554 wxMenuItemList::compatibility_iterator node
= menu
->GetMenuItems().GetFirst();
557 wxMenuItem
*item
= node
->GetData();
558 if (item
->IsSubMenu())
559 return FindMenuItemRecursive(item
->GetSubMenu(), menuString
, itemString
);
561 node
= node
->GetNext();
567 int wxMenuBar::FindMenuItem( const wxString
&menuString
, const wxString
&itemString
) const
569 wxMenuList::compatibility_iterator node
= m_menus
.GetFirst();
572 wxMenu
*menu
= node
->GetData();
573 int res
= FindMenuItemRecursive( menu
, menuString
, itemString
);
576 node
= node
->GetNext();
582 // Find a wxMenuItem using its id. Recurses down into sub-menus
583 static wxMenuItem
* FindMenuItemByIdRecursive(const wxMenu
* menu
, int id
)
585 wxMenuItem
* result
= menu
->FindChildItem(id
);
587 wxMenuItemList::compatibility_iterator node
= menu
->GetMenuItems().GetFirst();
588 while ( node
&& result
== NULL
)
590 wxMenuItem
*item
= node
->GetData();
591 if (item
->IsSubMenu())
593 result
= FindMenuItemByIdRecursive( item
->GetSubMenu(), id
);
595 node
= node
->GetNext();
601 wxMenuItem
* wxMenuBar::FindItem( int id
, wxMenu
**menuForItem
) const
603 wxMenuItem
* result
= 0;
604 wxMenuList::compatibility_iterator node
= m_menus
.GetFirst();
605 while (node
&& result
== 0)
607 wxMenu
*menu
= node
->GetData();
608 result
= FindMenuItemByIdRecursive( menu
, id
);
609 node
= node
->GetNext();
614 *menuForItem
= result
? result
->GetMenu() : (wxMenu
*)NULL
;
620 void wxMenuBar::EnableTop( size_t pos
, bool flag
)
622 wxMenuList::compatibility_iterator node
= m_menus
.Item( pos
);
624 wxCHECK_RET( node
, wxT("menu not found") );
626 wxMenu
* menu
= node
->GetData();
629 gtk_widget_set_sensitive( menu
->m_owner
, flag
);
632 wxString
wxMenuBar::GetLabelTop( size_t pos
) const
634 wxMenuList::compatibility_iterator node
= m_menus
.Item( pos
);
636 wxCHECK_MSG( node
, wxT("invalid"), wxT("menu not found") );
638 wxMenu
* menu
= node
->GetData();
641 wxString
text( menu
->GetTitle() );
642 for ( const wxChar
*pc
= text
.c_str(); *pc
; pc
++ )
644 if ( *pc
== wxT('_') )
646 // '_' is the escape character for GTK+
650 // don't remove ampersands '&' since if we have them in the menu title
651 // it means that they were doubled to indicate "&" instead of accelerator
659 void wxMenuBar::SetLabelTop( size_t pos
, const wxString
& label
)
661 wxMenuList::compatibility_iterator node
= m_menus
.Item( pos
);
663 wxCHECK_RET( node
, wxT("menu not found") );
665 wxMenu
* menu
= node
->GetData();
667 wxString
str( wxReplaceUnderscore( label
) );
669 menu
->SetTitle( str
);
673 GtkLabel
*label
= GTK_LABEL( GTK_BIN(menu
->m_owner
)->child
);
676 gtk_label_set( label
, wxGTK_CONV( str
) );
678 /* reparse key accel */
679 (void)gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV( str
) );
680 gtk_accel_label_refetch( GTK_ACCEL_LABEL(label
) );
685 //-----------------------------------------------------------------------------
687 //-----------------------------------------------------------------------------
689 static void gtk_menu_clicked_callback( GtkWidget
*widget
, wxMenu
*menu
)
692 wxapp_install_idle_handler();
694 int id
= menu
->FindMenuIdByMenuItem(widget
);
696 /* should find it for normal (not popup) menu */
697 wxASSERT_MSG( (id
!= -1) || (menu
->GetInvokingWindow() != NULL
),
698 _T("menu item not found in gtk_menu_clicked_callback") );
700 if (!menu
->IsEnabled(id
))
703 wxMenuItem
* item
= menu
->FindChildItem( id
);
704 wxCHECK_RET( item
, wxT("error in menu item callback") );
706 if (item
->IsCheckable())
708 bool isReallyChecked
= item
->IsChecked(),
709 isInternallyChecked
= item
->wxMenuItemBase::IsChecked();
711 // ensure that the internal state is always consistent with what is
712 // shown on the screen
713 item
->wxMenuItemBase::Check(isReallyChecked
);
715 // we must not report the events for the radio button going up nor the
716 // events resulting from the calls to wxMenuItem::Check()
717 if ( (item
->GetKind() == wxITEM_RADIO
&& !isReallyChecked
) ||
718 (isInternallyChecked
== isReallyChecked
) )
725 // Is this menu on a menubar? (possibly nested)
726 wxFrame
* frame
= NULL
;
728 while ( pm
&& !frame
)
730 if ( pm
->IsAttached() )
731 frame
= pm
->GetMenuBar()->GetFrame();
732 pm
= pm
->GetParent();
735 // FIXME: why do we have to call wxFrame::GetEventHandler() directly here?
736 // normally wxMenu::SendEvent() should be enough, if it doesn't work
737 // in wxGTK then we have a bug in wxMenu::GetInvokingWindow() which
738 // should be fixed instead of working around it here...
741 // If it is attached then let the frame send the event.
742 // Don't call frame->ProcessCommand(id) because it toggles
743 // checkable items and we've already done that above.
744 wxCommandEvent
commandEvent(wxEVT_COMMAND_MENU_SELECTED
, id
);
745 commandEvent
.SetEventObject(frame
);
746 if (item
->IsCheckable())
747 commandEvent
.SetInt(item
->IsChecked());
748 commandEvent
.SetEventObject(menu
);
750 frame
->GetEventHandler()->ProcessEvent(commandEvent
);
754 // otherwise let the menu have it
755 menu
->SendEvent(id
, item
->IsCheckable() ? item
->IsChecked() : -1);
759 //-----------------------------------------------------------------------------
761 //-----------------------------------------------------------------------------
763 static void gtk_menu_hilight_callback( GtkWidget
*widget
, wxMenu
*menu
)
765 if (g_isIdle
) wxapp_install_idle_handler();
767 int id
= menu
->FindMenuIdByMenuItem(widget
);
769 wxASSERT( id
!= -1 ); // should find it!
771 if (!menu
->IsEnabled(id
))
774 wxMenuEvent
event( wxEVT_MENU_HIGHLIGHT
, id
);
775 event
.SetEventObject( menu
);
777 wxEvtHandler
* handler
= menu
->GetEventHandler();
778 if (handler
&& handler
->ProcessEvent(event
))
781 wxWindow
*win
= menu
->GetInvokingWindow();
782 if (win
) win
->GetEventHandler()->ProcessEvent( event
);
785 //-----------------------------------------------------------------------------
787 //-----------------------------------------------------------------------------
789 static void gtk_menu_nolight_callback( GtkWidget
*widget
, wxMenu
*menu
)
791 if (g_isIdle
) wxapp_install_idle_handler();
793 int id
= menu
->FindMenuIdByMenuItem(widget
);
795 wxASSERT( id
!= -1 ); // should find it!
797 if (!menu
->IsEnabled(id
))
800 wxMenuEvent
event( wxEVT_MENU_HIGHLIGHT
, -1 );
801 event
.SetEventObject( menu
);
803 wxEvtHandler
* handler
= menu
->GetEventHandler();
804 if (handler
&& handler
->ProcessEvent(event
))
807 wxWindow
*win
= menu
->GetInvokingWindow();
809 win
->GetEventHandler()->ProcessEvent( event
);
812 //-----------------------------------------------------------------------------
814 //-----------------------------------------------------------------------------
816 IMPLEMENT_DYNAMIC_CLASS(wxMenuItem
, wxObject
)
818 wxMenuItem
*wxMenuItemBase::New(wxMenu
*parentMenu
,
820 const wxString
& name
,
821 const wxString
& help
,
825 return new wxMenuItem(parentMenu
, id
, name
, help
, kind
, subMenu
);
828 wxMenuItem::wxMenuItem(wxMenu
*parentMenu
,
830 const wxString
& text
,
831 const wxString
& help
,
834 : wxMenuItemBase(parentMenu
, id
, text
, help
, kind
, subMenu
)
839 wxMenuItem::wxMenuItem(wxMenu
*parentMenu
,
841 const wxString
& text
,
842 const wxString
& help
,
845 : wxMenuItemBase(parentMenu
, id
, text
, help
,
846 isCheckable
? wxITEM_CHECK
: wxITEM_NORMAL
, subMenu
)
851 void wxMenuItem::Init(const wxString
& text
)
853 m_labelWidget
= (GtkWidget
*) NULL
;
854 m_menuItem
= (GtkWidget
*) NULL
;
859 wxMenuItem::~wxMenuItem()
861 // don't delete menu items, the menus take care of that
864 // return the menu item text without any menu accels
866 wxString
wxMenuItemBase::GetLabelFromText(const wxString
& text
)
870 for ( const wxChar
*pc
= text
.c_str(); *pc
; pc
++ )
872 if ( *pc
== wxT('_') )
874 // GTK 1.2 escapes "xxx_xxx" to "xxx__xxx"
880 #if GTK_CHECK_VERSION(2, 0, 0)
881 if ( *pc
== wxT('\\') )
883 // GTK 2.0 escapes "xxx/xxx" to "xxx\/xxx"
890 if ( (*pc
== wxT('&')) && (*(pc
+1) != wxT('&')) )
893 // "&" is doubled to indicate "&" instead of accelerator
900 // wxPrintf( L"text %s label %s\n", text.c_str(), label.c_str() );
905 void wxMenuItem::SetText( const wxString
& str
)
907 // Some optimization to avoid flicker
908 wxString oldLabel
= m_text
;
909 oldLabel
= wxStripMenuCodes(oldLabel
.BeforeFirst('\t'));
910 oldLabel
.Replace(wxT("_"), wxT(""));
911 wxString label1
= wxStripMenuCodes(str
.BeforeFirst('\t'));
912 if (oldLabel
== label1
)
921 label
= (GtkLabel
*) m_labelWidget
;
923 label
= GTK_LABEL( GTK_BIN(m_menuItem
)->child
);
925 #if GTK_CHECK_VERSION(2, 0, 0)
926 // We have to imitate item_factory_unescape_label here
928 for (size_t n
= 0; n
< m_text
.Len(); n
++)
930 if (m_text
[n
] != wxT('\\'))
934 gtk_label_set_text_with_mnemonic( GTK_LABEL(label
), wxGTK_CONV(tmp
) );
937 gtk_label_set( label
, wxGTK_CONV( m_text
) );
940 (void)gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV(m_text
) );
941 gtk_accel_label_refetch( GTK_ACCEL_LABEL(label
) );
946 // it's valid for this function to be called even if m_menuItem == NULL
947 void wxMenuItem::DoSetText( const wxString
& str
)
949 // '\t' is the deliminator indicating a hot key
951 const wxChar
*pc
= str
;
952 while ( (*pc
!= wxT('\0')) && (*pc
!= wxT('\t')) )
954 if ((*pc
== wxT('&')) && (*(pc
+1) == wxT('&')))
956 // "&" is doubled to indicate "&" instead of accelerator
960 else if (*pc
== wxT('&'))
964 #if GTK_CHECK_VERSION(2, 0, 0)
965 else if ( *pc
== wxT('_') ) // escape underscores
967 // m_text << wxT("__"); doesn't work
970 else if (*pc
== wxT('/')) // we have to escape slashes
972 m_text
<< wxT("\\/");
974 else if (*pc
== wxT('\\')) // we have to double backslashes
976 m_text
<< wxT("\\\\");
979 else if ( *pc
== wxT('_') ) // escape underscores
983 else if (*pc
== wxT('/')) /* we have to filter out slashes ... */
985 m_text
<< wxT('\\'); /* ... and replace them with back slashes */
994 // wxPrintf( L"str %s m_text %s\n", str.c_str(), m_text.c_str() );
1007 wxAcceleratorEntry
*wxMenuItem::GetAccel() const
1012 return (wxAcceleratorEntry
*)NULL
;
1015 // as wxGetAccelFromString() looks for TAB, insert a dummy one here
1017 label
<< wxT('\t') << GetHotKey();
1019 return wxGetAccelFromString(label
);
1022 #endif // wxUSE_ACCEL
1024 void wxMenuItem::Check( bool check
)
1026 wxCHECK_RET( m_menuItem
, wxT("invalid menu item") );
1028 if (check
== m_isChecked
)
1031 wxMenuItemBase::Check( check
);
1033 switch ( GetKind() )
1037 gtk_check_menu_item_set_state( (GtkCheckMenuItem
*)m_menuItem
, (gint
)check
);
1041 wxFAIL_MSG( _T("can't check this item") );
1045 void wxMenuItem::Enable( bool enable
)
1047 wxCHECK_RET( m_menuItem
, wxT("invalid menu item") );
1049 gtk_widget_set_sensitive( m_menuItem
, enable
);
1050 wxMenuItemBase::Enable( enable
);
1053 bool wxMenuItem::IsChecked() const
1055 wxCHECK_MSG( m_menuItem
, FALSE
, wxT("invalid menu item") );
1057 wxCHECK_MSG( IsCheckable(), FALSE
,
1058 wxT("can't get state of uncheckable item!") );
1060 return ((GtkCheckMenuItem
*)m_menuItem
)->active
!= 0;
1063 wxString
wxMenuItem::GetFactoryPath() const
1065 // In order to get the pointer to the item we need the item
1066 // text _without_ underscores in GTK 1.2
1067 wxString
path( wxT("<main>/") );
1069 for ( const wxChar
*pc
= m_text
.c_str(); *pc
; pc
++ )
1071 if ( *pc
== wxT('_') )
1076 // remove '_' unconditionally
1081 // don't remove ampersands '&' since if we have them in the menu item title
1082 // it means that they were doubled to indicate "&" instead of accelerator
1090 //-----------------------------------------------------------------------------
1092 //-----------------------------------------------------------------------------
1094 IMPLEMENT_DYNAMIC_CLASS(wxMenu
,wxEvtHandler
)
1098 m_accel
= gtk_accel_group_new();
1099 m_factory
= gtk_item_factory_new( GTK_TYPE_MENU
, "<main>", m_accel
);
1100 m_menu
= gtk_item_factory_get_widget( m_factory
, "<main>" );
1102 m_owner
= (GtkWidget
*) NULL
;
1104 // Tearoffs are entries, just like separators. So if we want this
1105 // menu to be a tear-off one, we just append a tearoff entry
1107 if(m_style
& wxMENU_TEAROFF
)
1109 GtkItemFactoryEntry entry
;
1110 entry
.path
= (char *)"/tearoff";
1111 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
1112 entry
.callback_action
= 0;
1113 entry
.item_type
= (char *)"<Tearoff>";
1114 entry
.accelerator
= (gchar
*) NULL
;
1115 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
1116 //GtkWidget *menuItem = gtk_item_factory_get_widget( m_factory, "<main>/tearoff" );
1119 // append the title as the very first entry if we have it
1122 Append(-2, m_title
);
1129 WX_CLEAR_LIST(wxMenuItemList
, m_items
);
1131 if ( GTK_IS_WIDGET( m_menu
))
1132 gtk_widget_destroy( m_menu
);
1134 gtk_object_unref( GTK_OBJECT(m_factory
) );
1137 bool wxMenu::GtkAppend(wxMenuItem
*mitem
)
1139 GtkWidget
*menuItem
;
1141 #if defined(USE_MENU_BITMAPS) || !GTK_CHECK_VERSION(1, 2, 0)
1142 bool appended
= FALSE
;
1145 // does this item terminate the current radio group?
1146 bool endOfRadioGroup
= TRUE
;
1148 if ( mitem
->IsSeparator() )
1150 GtkItemFactoryEntry entry
;
1151 entry
.path
= (char *)"/sep";
1152 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
1153 entry
.callback_action
= 0;
1154 entry
.item_type
= (char *)"<Separator>";
1155 entry
.accelerator
= (gchar
*) NULL
;
1157 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
1159 // this will be wrong for more than one separator. do we care?
1160 menuItem
= gtk_item_factory_get_widget( m_factory
, "<main>/sep" );
1162 // we might have a separator inside a radio group
1163 endOfRadioGroup
= FALSE
;
1165 else if ( mitem
->IsSubMenu() )
1167 // text has "_" instead of "&" after mitem->SetText()
1168 wxString
text( mitem
->GetText() );
1170 // local buffer in multibyte form
1173 strcat( buf
, wxGTK_CONV( text
) );
1175 GtkItemFactoryEntry entry
;
1177 entry
.callback
= (GtkItemFactoryCallback
) 0;
1178 entry
.callback_action
= 0;
1179 entry
.item_type
= (char *)"<Branch>";
1180 entry
.accelerator
= (gchar
*) NULL
;
1182 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
1184 wxString
path( mitem
->GetFactoryPath() );
1185 menuItem
= gtk_item_factory_get_item( m_factory
, wxGTK_CONV( path
) );
1187 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menuItem
), mitem
->GetSubMenu()->m_menu
);
1189 // if adding a submenu to a menu already existing in the menu bar, we
1190 // must set invoking window to allow processing events from this
1192 if ( m_invokingWindow
)
1193 wxMenubarSetInvokingWindow(mitem
->GetSubMenu(), m_invokingWindow
);
1195 #ifdef USE_MENU_BITMAPS
1196 else if (mitem
->GetBitmap().Ok()) // An item with bitmap
1198 wxString
text( mitem
->GetText() );
1199 const wxBitmap
*bitmap
= &mitem
->GetBitmap();
1201 menuItem
= gtk_pixmap_menu_item_new ();
1202 GtkWidget
*label
= gtk_accel_label_new ( wxGTK_CONV( text
) );
1203 gtk_misc_set_alignment (GTK_MISC (label
), 0.0, 0.5);
1204 gtk_container_add (GTK_CONTAINER (menuItem
), label
);
1205 gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (label
), menuItem
);
1207 GdkModifierType accel_mods
;
1209 // accelerator for the item, as specified by its label
1210 // (ex. Ctrl+O for open)
1211 gtk_accelerator_parse(GetHotKey(*mitem
).c_str(),
1212 &accel_key
, &accel_mods
);
1213 if (accel_key
!= GDK_VoidSymbol
)
1215 gtk_widget_add_accelerator (menuItem
,
1217 gtk_menu_get_accel_group(
1219 accel_key
, accel_mods
,
1223 // accelerator for the underlined char (ex ALT+F for the File menu)
1224 accel_key
= gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV( text
) );
1225 if (accel_key
!= GDK_VoidSymbol
)
1227 gtk_widget_add_accelerator (menuItem
,
1229 gtk_menu_ensure_uline_accel_group (
1235 gtk_widget_show (label
);
1237 mitem
->SetLabelWidget(label
);
1239 GtkWidget
* pixmap
= gtk_pixmap_new( bitmap
->GetPixmap(), bitmap
->GetMask() ? bitmap
->GetMask()->GetBitmap() : (GdkBitmap
* )NULL
);
1240 gtk_widget_show(pixmap
);
1241 gtk_pixmap_menu_item_set_pixmap(GTK_PIXMAP_MENU_ITEM( menuItem
), pixmap
);
1243 gtk_signal_connect( GTK_OBJECT(menuItem
), "activate",
1244 GTK_SIGNAL_FUNC(gtk_menu_clicked_callback
),
1247 gtk_menu_append( GTK_MENU(m_menu
), menuItem
);
1249 gtk_widget_show( menuItem
);
1251 appended
= TRUE
; // We've done this, don't do it again
1253 #endif // USE_MENU_BITMAPS
1254 else // a normal item
1256 // text has "_" instead of "&" after mitem->SetText() so don't use it
1257 wxString
text( mitem
->GetText() );
1259 // buffers containing the menu item path and type in multibyte form
1263 strcpy( bufPath
, "/" );
1264 strncat( bufPath
, wxGTK_CONV(text
), WXSIZEOF(bufPath
) - 2 );
1265 bufPath
[WXSIZEOF(bufPath
) - 1] = '\0';
1267 GtkItemFactoryEntry entry
;
1268 entry
.path
= bufPath
;
1269 entry
.callback
= (GtkItemFactoryCallback
) gtk_menu_clicked_callback
;
1270 entry
.callback_action
= 0;
1273 const char *item_type
;
1274 switch ( mitem
->GetKind() )
1277 item_type
= "<CheckItem>";
1281 if ( m_pathLastRadio
.empty() )
1283 // start of a new radio group
1284 item_type
= "<RadioItem>";
1285 wxString
tmp( wxGTK_CONV_BACK( bufPath
) );
1287 m_pathLastRadio
= tmp
;
1289 else // continue the radio group
1291 pathRadio
= m_pathLastRadio
;
1292 pathRadio
.Replace(wxT("_"), wxT(""));
1293 pathRadio
.Prepend(wxT("<main>/"));
1295 strncpy(bufType
, wxGTK_CONV(pathRadio
), WXSIZEOF(bufType
));
1296 bufType
[WXSIZEOF(bufType
) - 1] = '\0';
1297 item_type
= bufType
;
1300 // continue the existing radio group, if any
1301 endOfRadioGroup
= FALSE
;
1305 wxFAIL_MSG( _T("unexpected menu item kind") );
1309 item_type
= "<Item>";
1313 entry
.item_type
= (char *)item_type
; // cast needed for GTK+
1314 entry
.accelerator
= (gchar
*) NULL
;
1317 // due to an apparent bug in GTK+, we have to use a static buffer here -
1318 // otherwise GTK+ 1.2.2 manages to override the memory we pass to it
1320 char s_accel
[50]; // should be big enough, we check for overruns
1321 wxString
tmp( GetHotKey(*mitem
) );
1322 strncpy(s_accel
, wxGTK_CONV( tmp
), WXSIZEOF(s_accel
));
1323 s_accel
[WXSIZEOF(s_accel
) - 1] = '\0';
1324 entry
.accelerator
= s_accel
;
1325 #else // !wxUSE_ACCEL
1326 entry
.accelerator
= (char*) NULL
;
1327 #endif // wxUSE_ACCEL/!wxUSE_ACCEL
1329 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); /* what is 2 ? */
1331 wxString
path( mitem
->GetFactoryPath() );
1332 menuItem
= gtk_item_factory_get_widget( m_factory
, wxGTK_CONV( path
) );
1335 wxLogError( wxT("Wrong menu path: %s\n"), path
.c_str() );
1338 if ( !mitem
->IsSeparator() )
1340 wxASSERT_MSG( menuItem
, wxT("invalid menuitem") );
1342 gtk_signal_connect( GTK_OBJECT(menuItem
), "select",
1343 GTK_SIGNAL_FUNC(gtk_menu_hilight_callback
),
1346 gtk_signal_connect( GTK_OBJECT(menuItem
), "deselect",
1347 GTK_SIGNAL_FUNC(gtk_menu_nolight_callback
),
1351 mitem
->SetMenuItem(menuItem
);
1353 if ( endOfRadioGroup
)
1355 m_pathLastRadio
.clear();
1361 bool wxMenu::DoAppend(wxMenuItem
*mitem
)
1363 return GtkAppend(mitem
) && wxMenuBase::DoAppend(mitem
);
1366 bool wxMenu::DoInsert(size_t pos
, wxMenuItem
*item
)
1368 if ( !wxMenuBase::DoInsert(pos
, item
) )
1371 // GTK+ doesn't have a function to insert a menu using GtkItemFactory (as
1372 // of version 1.2.6), so we first append the item and then change its
1374 if ( !GtkAppend(item
) )
1377 if ( m_style
& wxMENU_TEAROFF
)
1379 // change the position as the first item is the tear-off marker
1383 GtkMenuShell
*menu_shell
= GTK_MENU_SHELL(m_factory
->widget
);
1384 gpointer data
= g_list_last(menu_shell
->children
)->data
;
1385 menu_shell
->children
= g_list_remove(menu_shell
->children
, data
);
1386 menu_shell
->children
= g_list_insert(menu_shell
->children
, data
, pos
);
1391 wxMenuItem
*wxMenu::DoRemove(wxMenuItem
*item
)
1393 if ( !wxMenuBase::DoRemove(item
) )
1394 return (wxMenuItem
*)NULL
;
1396 // TODO: this code doesn't delete the item factory item and this seems
1397 // impossible as of GTK 1.2.6.
1398 gtk_widget_destroy( item
->GetMenuItem() );
1403 int wxMenu::FindMenuIdByMenuItem( GtkWidget
*menuItem
) const
1405 wxMenuItemList::compatibility_iterator node
= m_items
.GetFirst();
1408 wxMenuItem
*item
= node
->GetData();
1409 if (item
->GetMenuItem() == menuItem
)
1410 return item
->GetId();
1411 node
= node
->GetNext();
1417 // ----------------------------------------------------------------------------
1419 // ----------------------------------------------------------------------------
1421 #if GTK_CHECK_VERSION(1, 2, 0) && wxUSE_ACCEL
1423 static wxString
GetHotKey( const wxMenuItem
& item
)
1427 wxAcceleratorEntry
*accel
= item
.GetAccel();
1430 int flags
= accel
->GetFlags();
1431 if ( flags
& wxACCEL_ALT
)
1432 hotkey
+= wxT("<alt>");
1433 if ( flags
& wxACCEL_CTRL
)
1434 hotkey
+= wxT("<control>");
1435 if ( flags
& wxACCEL_SHIFT
)
1436 hotkey
+= wxT("<shift>");
1438 int code
= accel
->GetKeyCode();
1453 hotkey
<< wxT('F') << code
- WXK_F1
+ 1;
1456 // TODO: we should use gdk_keyval_name() (a.k.a.
1457 // XKeysymToString) here as well as hardcoding the keysym
1458 // names this might be not portable
1459 case WXK_NUMPAD_INSERT
:
1460 hotkey
<< wxT("KP_Insert" );
1462 case WXK_NUMPAD_DELETE
:
1463 hotkey
<< wxT("KP_Delete" );
1466 hotkey
<< wxT("Insert" );
1469 hotkey
<< wxT("Delete" );
1472 hotkey
<< wxT("Up" );
1475 hotkey
<< wxT("Down" );
1478 hotkey
<< wxT("Prior" );
1481 hotkey
<< wxT("Next" );
1484 hotkey
<< wxT("Left" );
1487 hotkey
<< wxT("Right" );
1490 hotkey
<< wxT("Home" );
1493 hotkey
<< wxT("End" );
1496 hotkey
<< wxT("Return" );
1499 // if there are any other keys wxGetAccelFromString() may
1500 // return, we should process them here
1505 wxString name
= wxGTK_CONV_BACK( gdk_keyval_name((guint
)code
) );
1513 wxFAIL_MSG( wxT("unknown keyboard accel") );
1522 #endif // wxUSE_ACCEL
1525 //-----------------------------------------------------------------------------
1526 // substitute for missing GtkPixmapMenuItem
1527 //-----------------------------------------------------------------------------
1529 #ifdef USE_MENU_BITMAPS
1532 * Copyright (C) 1998, 1999, 2000 Free Software Foundation
1533 * All rights reserved.
1535 * This file is part of the Gnome Library.
1537 * The Gnome Library is free software; you can redistribute it and/or
1538 * modify it under the terms of the GNU Library General Public License as
1539 * published by the Free Software Foundation; either version 2 of the
1540 * License, or (at your option) any later version.
1542 * The Gnome Library is distributed in the hope that it will be useful,
1543 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1544 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1545 * Library General Public License for more details.
1547 * You should have received a copy of the GNU Library General Public
1548 * License along with the Gnome Library; see the file COPYING.LIB. If not,
1549 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
1550 * Boston, MA 02111-1307, USA.
1556 /* Author: Dietmar Maurer <dm@vlsivie.tuwien.ac.at> */
1558 #include <gtk/gtkaccellabel.h>
1559 #include <gtk/gtksignal.h>
1560 #include <gtk/gtkmenuitem.h>
1561 #include <gtk/gtkmenu.h>
1562 #include <gtk/gtkcontainer.h>
1567 static void gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass
*klass
);
1568 static void gtk_pixmap_menu_item_init (GtkPixmapMenuItem
*menu_item
);
1569 static void gtk_pixmap_menu_item_draw (GtkWidget
*widget
,
1570 GdkRectangle
*area
);
1571 static gint
gtk_pixmap_menu_item_expose (GtkWidget
*widget
,
1572 GdkEventExpose
*event
);
1574 /* we must override the following functions */
1576 static void gtk_pixmap_menu_item_map (GtkWidget
*widget
);
1577 static void gtk_pixmap_menu_item_size_allocate (GtkWidget
*widget
,
1578 GtkAllocation
*allocation
);
1579 static void gtk_pixmap_menu_item_forall (GtkContainer
*container
,
1580 gboolean include_internals
,
1581 GtkCallback callback
,
1582 gpointer callback_data
);
1583 static void gtk_pixmap_menu_item_size_request (GtkWidget
*widget
,
1584 GtkRequisition
*requisition
);
1585 static void gtk_pixmap_menu_item_remove (GtkContainer
*container
,
1588 static void changed_have_pixmap_status (GtkPixmapMenuItem
*menu_item
);
1590 static GtkMenuItemClass
*parent_class
= NULL
;
1594 #define BORDER_SPACING 3
1595 #define PMAP_WIDTH 20
1598 gtk_pixmap_menu_item_get_type (void)
1600 static GtkType pixmap_menu_item_type
= 0;
1602 if (!pixmap_menu_item_type
)
1604 GtkTypeInfo pixmap_menu_item_info
=
1606 (char *)"GtkPixmapMenuItem",
1607 sizeof (GtkPixmapMenuItem
),
1608 sizeof (GtkPixmapMenuItemClass
),
1609 (GtkClassInitFunc
) gtk_pixmap_menu_item_class_init
,
1610 (GtkObjectInitFunc
) gtk_pixmap_menu_item_init
,
1611 /* reserved_1 */ NULL
,
1612 /* reserved_2 */ NULL
,
1613 (GtkClassInitFunc
) NULL
,
1616 pixmap_menu_item_type
= gtk_type_unique (gtk_menu_item_get_type (),
1617 &pixmap_menu_item_info
);
1620 return pixmap_menu_item_type
;
1624 * gtk_pixmap_menu_item_new
1626 * Creates a new pixmap menu item. Use gtk_pixmap_menu_item_set_pixmap()
1627 * to set the pixmap wich is displayed at the left side.
1630 * &GtkWidget pointer to new menu item
1634 gtk_pixmap_menu_item_new (void)
1636 return GTK_WIDGET (gtk_type_new (gtk_pixmap_menu_item_get_type ()));
1640 gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass
*klass
)
1642 GtkObjectClass
*object_class
;
1643 GtkWidgetClass
*widget_class
;
1644 GtkMenuItemClass
*menu_item_class
;
1645 GtkContainerClass
*container_class
;
1647 object_class
= (GtkObjectClass
*) klass
;
1648 widget_class
= (GtkWidgetClass
*) klass
;
1649 menu_item_class
= (GtkMenuItemClass
*) klass
;
1650 container_class
= (GtkContainerClass
*) klass
;
1652 parent_class
= (GtkMenuItemClass
*) gtk_type_class (gtk_menu_item_get_type ());
1654 widget_class
->draw
= gtk_pixmap_menu_item_draw
;
1655 widget_class
->expose_event
= gtk_pixmap_menu_item_expose
;
1656 widget_class
->map
= gtk_pixmap_menu_item_map
;
1657 widget_class
->size_allocate
= gtk_pixmap_menu_item_size_allocate
;
1658 widget_class
->size_request
= gtk_pixmap_menu_item_size_request
;
1660 container_class
->forall
= gtk_pixmap_menu_item_forall
;
1661 container_class
->remove
= gtk_pixmap_menu_item_remove
;
1663 klass
->orig_toggle_size
= menu_item_class
->toggle_size
;
1664 klass
->have_pixmap_count
= 0;
1668 gtk_pixmap_menu_item_init (GtkPixmapMenuItem
*menu_item
)
1672 mi
= GTK_MENU_ITEM (menu_item
);
1674 menu_item
->pixmap
= NULL
;
1678 gtk_pixmap_menu_item_draw (GtkWidget
*widget
,
1681 g_return_if_fail (widget
!= NULL
);
1682 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
));
1683 g_return_if_fail (area
!= NULL
);
1685 if (GTK_WIDGET_CLASS (parent_class
)->draw
)
1686 (* GTK_WIDGET_CLASS (parent_class
)->draw
) (widget
, area
);
1688 if (GTK_WIDGET_DRAWABLE (widget
) &&
1689 GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
) {
1690 gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
),NULL
);
1695 gtk_pixmap_menu_item_expose (GtkWidget
*widget
,
1696 GdkEventExpose
*event
)
1698 g_return_val_if_fail (widget
!= NULL
, FALSE
);
1699 g_return_val_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
), FALSE
);
1700 g_return_val_if_fail (event
!= NULL
, FALSE
);
1702 if (GTK_WIDGET_CLASS (parent_class
)->expose_event
)
1703 (* GTK_WIDGET_CLASS (parent_class
)->expose_event
) (widget
, event
);
1705 if (GTK_WIDGET_DRAWABLE (widget
) &&
1706 GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
) {
1707 gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
),NULL
);
1714 * gtk_pixmap_menu_item_set_pixmap
1715 * @menu_item: Pointer to the pixmap menu item
1716 * @pixmap: Pointer to a pixmap widget
1718 * Set the pixmap of the menu item.
1723 gtk_pixmap_menu_item_set_pixmap (GtkPixmapMenuItem
*menu_item
,
1726 g_return_if_fail (menu_item
!= NULL
);
1727 g_return_if_fail (pixmap
!= NULL
);
1728 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (menu_item
));
1729 g_return_if_fail (GTK_IS_WIDGET (pixmap
));
1730 g_return_if_fail (menu_item
->pixmap
== NULL
);
1732 gtk_widget_set_parent (pixmap
, GTK_WIDGET (menu_item
));
1733 menu_item
->pixmap
= pixmap
;
1735 if (GTK_WIDGET_REALIZED (pixmap
->parent
) &&
1736 !GTK_WIDGET_REALIZED (pixmap
))
1737 gtk_widget_realize (pixmap
);
1739 if (GTK_WIDGET_VISIBLE (pixmap
->parent
)) {
1740 if (GTK_WIDGET_MAPPED (pixmap
->parent
) &&
1741 GTK_WIDGET_VISIBLE(pixmap
) &&
1742 !GTK_WIDGET_MAPPED (pixmap
))
1743 gtk_widget_map (pixmap
);
1746 changed_have_pixmap_status(menu_item
);
1748 if (GTK_WIDGET_VISIBLE (pixmap
) && GTK_WIDGET_VISIBLE (menu_item
))
1749 gtk_widget_queue_resize (pixmap
);
1753 gtk_pixmap_menu_item_map (GtkWidget
*widget
)
1755 GtkPixmapMenuItem
*menu_item
;
1757 g_return_if_fail (widget
!= NULL
);
1758 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
));
1760 menu_item
= GTK_PIXMAP_MENU_ITEM(widget
);
1762 GTK_WIDGET_CLASS(parent_class
)->map(widget
);
1764 if (menu_item
->pixmap
&&
1765 GTK_WIDGET_VISIBLE (menu_item
->pixmap
) &&
1766 !GTK_WIDGET_MAPPED (menu_item
->pixmap
))
1767 gtk_widget_map (menu_item
->pixmap
);
1771 gtk_pixmap_menu_item_size_allocate (GtkWidget
*widget
,
1772 GtkAllocation
*allocation
)
1774 GtkPixmapMenuItem
*pmenu_item
;
1776 pmenu_item
= GTK_PIXMAP_MENU_ITEM(widget
);
1778 if (pmenu_item
->pixmap
&& GTK_WIDGET_VISIBLE(pmenu_item
))
1780 GtkAllocation child_allocation
;
1783 border_width
= GTK_CONTAINER (widget
)->border_width
;
1785 child_allocation
.width
= pmenu_item
->pixmap
->requisition
.width
;
1786 child_allocation
.height
= pmenu_item
->pixmap
->requisition
.height
;
1787 child_allocation
.x
= border_width
+ BORDER_SPACING
;
1788 child_allocation
.y
= (border_width
+ BORDER_SPACING
1789 + (((allocation
->height
- child_allocation
.height
) - child_allocation
.x
)
1790 / 2)); /* center pixmaps vertically */
1791 gtk_widget_size_allocate (pmenu_item
->pixmap
, &child_allocation
);
1794 if (GTK_WIDGET_CLASS (parent_class
)->size_allocate
)
1795 GTK_WIDGET_CLASS(parent_class
)->size_allocate (widget
, allocation
);
1799 gtk_pixmap_menu_item_forall (GtkContainer
*container
,
1800 gboolean include_internals
,
1801 GtkCallback callback
,
1802 gpointer callback_data
)
1804 GtkPixmapMenuItem
*menu_item
;
1806 g_return_if_fail (container
!= NULL
);
1807 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container
));
1808 g_return_if_fail (callback
!= NULL
);
1810 menu_item
= GTK_PIXMAP_MENU_ITEM (container
);
1812 if (menu_item
->pixmap
)
1813 (* callback
) (menu_item
->pixmap
, callback_data
);
1815 GTK_CONTAINER_CLASS(parent_class
)->forall(container
,include_internals
,
1816 callback
,callback_data
);
1820 gtk_pixmap_menu_item_size_request (GtkWidget
*widget
,
1821 GtkRequisition
*requisition
)
1823 GtkPixmapMenuItem
*menu_item
;
1824 GtkRequisition req
= {0, 0};
1826 g_return_if_fail (widget
!= NULL
);
1827 g_return_if_fail (GTK_IS_MENU_ITEM (widget
));
1828 g_return_if_fail (requisition
!= NULL
);
1830 GTK_WIDGET_CLASS(parent_class
)->size_request(widget
,requisition
);
1832 menu_item
= GTK_PIXMAP_MENU_ITEM (widget
);
1834 if (menu_item
->pixmap
)
1835 gtk_widget_size_request(menu_item
->pixmap
, &req
);
1837 requisition
->height
= MAX(req
.height
+ GTK_CONTAINER(widget
)->border_width
+ BORDER_SPACING
, (unsigned int) requisition
->height
);
1838 requisition
->width
+= (req
.width
+ GTK_CONTAINER(widget
)->border_width
+ BORDER_SPACING
);
1842 gtk_pixmap_menu_item_remove (GtkContainer
*container
,
1846 gboolean widget_was_visible
;
1848 g_return_if_fail (container
!= NULL
);
1849 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container
));
1850 g_return_if_fail (child
!= NULL
);
1851 g_return_if_fail (GTK_IS_WIDGET (child
));
1853 bin
= GTK_BIN (container
);
1854 g_return_if_fail ((bin
->child
== child
||
1855 (GTK_PIXMAP_MENU_ITEM(container
)->pixmap
== child
)));
1857 widget_was_visible
= GTK_WIDGET_VISIBLE (child
);
1859 gtk_widget_unparent (child
);
1860 if (bin
->child
== child
)
1863 GTK_PIXMAP_MENU_ITEM(container
)->pixmap
= NULL
;
1864 changed_have_pixmap_status(GTK_PIXMAP_MENU_ITEM(container
));
1867 if (widget_was_visible
)
1868 gtk_widget_queue_resize (GTK_WIDGET (container
));
1872 /* important to only call this if there was actually a _change_ in pixmap == NULL */
1874 changed_have_pixmap_status (GtkPixmapMenuItem
*menu_item
)
1876 if (menu_item
->pixmap
!= NULL
) {
1877 GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
+= 1;
1879 if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
== 1) {
1880 /* Install pixmap toggle size */
1881 GTK_MENU_ITEM_GET_CLASS(menu_item
)->toggle_size
= MAX(GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->orig_toggle_size
, PMAP_WIDTH
);
1884 GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
-= 1;
1886 if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
== 0) {
1887 /* Install normal toggle size */
1888 GTK_MENU_ITEM_GET_CLASS(menu_item
)->toggle_size
= GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->orig_toggle_size
;
1892 /* Note that we actually need to do this for _all_ GtkPixmapMenuItem
1893 whenever the klass->toggle_size changes; but by doing it anytime
1894 this function is called, we get the same effect, just because of
1895 how the preferences option to show pixmaps works. Bogus, broken.
1897 if (GTK_WIDGET_VISIBLE(GTK_WIDGET(menu_item
)))
1898 gtk_widget_queue_resize(GTK_WIDGET(menu_item
));
1901 #endif // USE_MENU_BITMAPS