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" );
1479 hotkey
<< wxT("Prior" );
1483 hotkey
<< wxT("Next" );
1486 hotkey
<< wxT("Left" );
1489 hotkey
<< wxT("Right" );
1492 hotkey
<< wxT("Home" );
1495 hotkey
<< wxT("End" );
1498 hotkey
<< wxT("Return" );
1501 // if there are any other keys wxGetAccelFromString() may
1502 // return, we should process them here
1507 wxString name
= wxGTK_CONV_BACK( gdk_keyval_name((guint
)code
) );
1515 wxFAIL_MSG( wxT("unknown keyboard accel") );
1524 #endif // wxUSE_ACCEL
1527 //-----------------------------------------------------------------------------
1528 // substitute for missing GtkPixmapMenuItem
1529 //-----------------------------------------------------------------------------
1531 #ifdef USE_MENU_BITMAPS
1534 * Copyright (C) 1998, 1999, 2000 Free Software Foundation
1535 * All rights reserved.
1537 * This file is part of the Gnome Library.
1539 * The Gnome Library is free software; you can redistribute it and/or
1540 * modify it under the terms of the GNU Library General Public License as
1541 * published by the Free Software Foundation; either version 2 of the
1542 * License, or (at your option) any later version.
1544 * The Gnome Library is distributed in the hope that it will be useful,
1545 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1546 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1547 * Library General Public License for more details.
1549 * You should have received a copy of the GNU Library General Public
1550 * License along with the Gnome Library; see the file COPYING.LIB. If not,
1551 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
1552 * Boston, MA 02111-1307, USA.
1558 /* Author: Dietmar Maurer <dm@vlsivie.tuwien.ac.at> */
1560 #include <gtk/gtkaccellabel.h>
1561 #include <gtk/gtksignal.h>
1562 #include <gtk/gtkmenuitem.h>
1563 #include <gtk/gtkmenu.h>
1564 #include <gtk/gtkcontainer.h>
1569 static void gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass
*klass
);
1570 static void gtk_pixmap_menu_item_init (GtkPixmapMenuItem
*menu_item
);
1571 static void gtk_pixmap_menu_item_draw (GtkWidget
*widget
,
1572 GdkRectangle
*area
);
1573 static gint
gtk_pixmap_menu_item_expose (GtkWidget
*widget
,
1574 GdkEventExpose
*event
);
1576 /* we must override the following functions */
1578 static void gtk_pixmap_menu_item_map (GtkWidget
*widget
);
1579 static void gtk_pixmap_menu_item_size_allocate (GtkWidget
*widget
,
1580 GtkAllocation
*allocation
);
1581 static void gtk_pixmap_menu_item_forall (GtkContainer
*container
,
1582 gboolean include_internals
,
1583 GtkCallback callback
,
1584 gpointer callback_data
);
1585 static void gtk_pixmap_menu_item_size_request (GtkWidget
*widget
,
1586 GtkRequisition
*requisition
);
1587 static void gtk_pixmap_menu_item_remove (GtkContainer
*container
,
1590 static void changed_have_pixmap_status (GtkPixmapMenuItem
*menu_item
);
1592 static GtkMenuItemClass
*parent_class
= NULL
;
1596 #define BORDER_SPACING 3
1597 #define PMAP_WIDTH 20
1600 gtk_pixmap_menu_item_get_type (void)
1602 static GtkType pixmap_menu_item_type
= 0;
1604 if (!pixmap_menu_item_type
)
1606 GtkTypeInfo pixmap_menu_item_info
=
1608 (char *)"GtkPixmapMenuItem",
1609 sizeof (GtkPixmapMenuItem
),
1610 sizeof (GtkPixmapMenuItemClass
),
1611 (GtkClassInitFunc
) gtk_pixmap_menu_item_class_init
,
1612 (GtkObjectInitFunc
) gtk_pixmap_menu_item_init
,
1613 /* reserved_1 */ NULL
,
1614 /* reserved_2 */ NULL
,
1615 (GtkClassInitFunc
) NULL
,
1618 pixmap_menu_item_type
= gtk_type_unique (gtk_menu_item_get_type (),
1619 &pixmap_menu_item_info
);
1622 return pixmap_menu_item_type
;
1626 * gtk_pixmap_menu_item_new
1628 * Creates a new pixmap menu item. Use gtk_pixmap_menu_item_set_pixmap()
1629 * to set the pixmap wich is displayed at the left side.
1632 * &GtkWidget pointer to new menu item
1636 gtk_pixmap_menu_item_new (void)
1638 return GTK_WIDGET (gtk_type_new (gtk_pixmap_menu_item_get_type ()));
1642 gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass
*klass
)
1644 GtkObjectClass
*object_class
;
1645 GtkWidgetClass
*widget_class
;
1646 GtkMenuItemClass
*menu_item_class
;
1647 GtkContainerClass
*container_class
;
1649 object_class
= (GtkObjectClass
*) klass
;
1650 widget_class
= (GtkWidgetClass
*) klass
;
1651 menu_item_class
= (GtkMenuItemClass
*) klass
;
1652 container_class
= (GtkContainerClass
*) klass
;
1654 parent_class
= (GtkMenuItemClass
*) gtk_type_class (gtk_menu_item_get_type ());
1656 widget_class
->draw
= gtk_pixmap_menu_item_draw
;
1657 widget_class
->expose_event
= gtk_pixmap_menu_item_expose
;
1658 widget_class
->map
= gtk_pixmap_menu_item_map
;
1659 widget_class
->size_allocate
= gtk_pixmap_menu_item_size_allocate
;
1660 widget_class
->size_request
= gtk_pixmap_menu_item_size_request
;
1662 container_class
->forall
= gtk_pixmap_menu_item_forall
;
1663 container_class
->remove
= gtk_pixmap_menu_item_remove
;
1665 klass
->orig_toggle_size
= menu_item_class
->toggle_size
;
1666 klass
->have_pixmap_count
= 0;
1670 gtk_pixmap_menu_item_init (GtkPixmapMenuItem
*menu_item
)
1674 mi
= GTK_MENU_ITEM (menu_item
);
1676 menu_item
->pixmap
= NULL
;
1680 gtk_pixmap_menu_item_draw (GtkWidget
*widget
,
1683 g_return_if_fail (widget
!= NULL
);
1684 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
));
1685 g_return_if_fail (area
!= NULL
);
1687 if (GTK_WIDGET_CLASS (parent_class
)->draw
)
1688 (* GTK_WIDGET_CLASS (parent_class
)->draw
) (widget
, area
);
1690 if (GTK_WIDGET_DRAWABLE (widget
) &&
1691 GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
) {
1692 gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
),NULL
);
1697 gtk_pixmap_menu_item_expose (GtkWidget
*widget
,
1698 GdkEventExpose
*event
)
1700 g_return_val_if_fail (widget
!= NULL
, FALSE
);
1701 g_return_val_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
), FALSE
);
1702 g_return_val_if_fail (event
!= NULL
, FALSE
);
1704 if (GTK_WIDGET_CLASS (parent_class
)->expose_event
)
1705 (* GTK_WIDGET_CLASS (parent_class
)->expose_event
) (widget
, event
);
1707 if (GTK_WIDGET_DRAWABLE (widget
) &&
1708 GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
) {
1709 gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
),NULL
);
1716 * gtk_pixmap_menu_item_set_pixmap
1717 * @menu_item: Pointer to the pixmap menu item
1718 * @pixmap: Pointer to a pixmap widget
1720 * Set the pixmap of the menu item.
1725 gtk_pixmap_menu_item_set_pixmap (GtkPixmapMenuItem
*menu_item
,
1728 g_return_if_fail (menu_item
!= NULL
);
1729 g_return_if_fail (pixmap
!= NULL
);
1730 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (menu_item
));
1731 g_return_if_fail (GTK_IS_WIDGET (pixmap
));
1732 g_return_if_fail (menu_item
->pixmap
== NULL
);
1734 gtk_widget_set_parent (pixmap
, GTK_WIDGET (menu_item
));
1735 menu_item
->pixmap
= pixmap
;
1737 if (GTK_WIDGET_REALIZED (pixmap
->parent
) &&
1738 !GTK_WIDGET_REALIZED (pixmap
))
1739 gtk_widget_realize (pixmap
);
1741 if (GTK_WIDGET_VISIBLE (pixmap
->parent
)) {
1742 if (GTK_WIDGET_MAPPED (pixmap
->parent
) &&
1743 GTK_WIDGET_VISIBLE(pixmap
) &&
1744 !GTK_WIDGET_MAPPED (pixmap
))
1745 gtk_widget_map (pixmap
);
1748 changed_have_pixmap_status(menu_item
);
1750 if (GTK_WIDGET_VISIBLE (pixmap
) && GTK_WIDGET_VISIBLE (menu_item
))
1751 gtk_widget_queue_resize (pixmap
);
1755 gtk_pixmap_menu_item_map (GtkWidget
*widget
)
1757 GtkPixmapMenuItem
*menu_item
;
1759 g_return_if_fail (widget
!= NULL
);
1760 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
));
1762 menu_item
= GTK_PIXMAP_MENU_ITEM(widget
);
1764 GTK_WIDGET_CLASS(parent_class
)->map(widget
);
1766 if (menu_item
->pixmap
&&
1767 GTK_WIDGET_VISIBLE (menu_item
->pixmap
) &&
1768 !GTK_WIDGET_MAPPED (menu_item
->pixmap
))
1769 gtk_widget_map (menu_item
->pixmap
);
1773 gtk_pixmap_menu_item_size_allocate (GtkWidget
*widget
,
1774 GtkAllocation
*allocation
)
1776 GtkPixmapMenuItem
*pmenu_item
;
1778 pmenu_item
= GTK_PIXMAP_MENU_ITEM(widget
);
1780 if (pmenu_item
->pixmap
&& GTK_WIDGET_VISIBLE(pmenu_item
))
1782 GtkAllocation child_allocation
;
1785 border_width
= GTK_CONTAINER (widget
)->border_width
;
1787 child_allocation
.width
= pmenu_item
->pixmap
->requisition
.width
;
1788 child_allocation
.height
= pmenu_item
->pixmap
->requisition
.height
;
1789 child_allocation
.x
= border_width
+ BORDER_SPACING
;
1790 child_allocation
.y
= (border_width
+ BORDER_SPACING
1791 + (((allocation
->height
- child_allocation
.height
) - child_allocation
.x
)
1792 / 2)); /* center pixmaps vertically */
1793 gtk_widget_size_allocate (pmenu_item
->pixmap
, &child_allocation
);
1796 if (GTK_WIDGET_CLASS (parent_class
)->size_allocate
)
1797 GTK_WIDGET_CLASS(parent_class
)->size_allocate (widget
, allocation
);
1801 gtk_pixmap_menu_item_forall (GtkContainer
*container
,
1802 gboolean include_internals
,
1803 GtkCallback callback
,
1804 gpointer callback_data
)
1806 GtkPixmapMenuItem
*menu_item
;
1808 g_return_if_fail (container
!= NULL
);
1809 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container
));
1810 g_return_if_fail (callback
!= NULL
);
1812 menu_item
= GTK_PIXMAP_MENU_ITEM (container
);
1814 if (menu_item
->pixmap
)
1815 (* callback
) (menu_item
->pixmap
, callback_data
);
1817 GTK_CONTAINER_CLASS(parent_class
)->forall(container
,include_internals
,
1818 callback
,callback_data
);
1822 gtk_pixmap_menu_item_size_request (GtkWidget
*widget
,
1823 GtkRequisition
*requisition
)
1825 GtkPixmapMenuItem
*menu_item
;
1826 GtkRequisition req
= {0, 0};
1828 g_return_if_fail (widget
!= NULL
);
1829 g_return_if_fail (GTK_IS_MENU_ITEM (widget
));
1830 g_return_if_fail (requisition
!= NULL
);
1832 GTK_WIDGET_CLASS(parent_class
)->size_request(widget
,requisition
);
1834 menu_item
= GTK_PIXMAP_MENU_ITEM (widget
);
1836 if (menu_item
->pixmap
)
1837 gtk_widget_size_request(menu_item
->pixmap
, &req
);
1839 requisition
->height
= MAX(req
.height
+ GTK_CONTAINER(widget
)->border_width
+ BORDER_SPACING
, (unsigned int) requisition
->height
);
1840 requisition
->width
+= (req
.width
+ GTK_CONTAINER(widget
)->border_width
+ BORDER_SPACING
);
1844 gtk_pixmap_menu_item_remove (GtkContainer
*container
,
1848 gboolean widget_was_visible
;
1850 g_return_if_fail (container
!= NULL
);
1851 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container
));
1852 g_return_if_fail (child
!= NULL
);
1853 g_return_if_fail (GTK_IS_WIDGET (child
));
1855 bin
= GTK_BIN (container
);
1856 g_return_if_fail ((bin
->child
== child
||
1857 (GTK_PIXMAP_MENU_ITEM(container
)->pixmap
== child
)));
1859 widget_was_visible
= GTK_WIDGET_VISIBLE (child
);
1861 gtk_widget_unparent (child
);
1862 if (bin
->child
== child
)
1865 GTK_PIXMAP_MENU_ITEM(container
)->pixmap
= NULL
;
1866 changed_have_pixmap_status(GTK_PIXMAP_MENU_ITEM(container
));
1869 if (widget_was_visible
)
1870 gtk_widget_queue_resize (GTK_WIDGET (container
));
1874 /* important to only call this if there was actually a _change_ in pixmap == NULL */
1876 changed_have_pixmap_status (GtkPixmapMenuItem
*menu_item
)
1878 if (menu_item
->pixmap
!= NULL
) {
1879 GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
+= 1;
1881 if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
== 1) {
1882 /* Install pixmap toggle size */
1883 GTK_MENU_ITEM_GET_CLASS(menu_item
)->toggle_size
= MAX(GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->orig_toggle_size
, PMAP_WIDTH
);
1886 GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
-= 1;
1888 if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
== 0) {
1889 /* Install normal toggle size */
1890 GTK_MENU_ITEM_GET_CLASS(menu_item
)->toggle_size
= GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->orig_toggle_size
;
1894 /* Note that we actually need to do this for _all_ GtkPixmapMenuItem
1895 whenever the klass->toggle_size changes; but by doing it anytime
1896 this function is called, we get the same effect, just because of
1897 how the preferences option to show pixmaps works. Bogus, broken.
1899 if (GTK_WIDGET_VISIBLE(GTK_WIDGET(menu_item
)))
1900 gtk_widget_queue_resize(GTK_WIDGET(menu_item
));
1903 #endif // USE_MENU_BITMAPS