1 /////////////////////////////////////////////////////////////////////////////
4 // Author: Robert Roebling
6 // Copyright: (c) 1998 Robert Roebling
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
11 #pragma implementation "menu.h"
12 #pragma implementation "menuitem.h"
18 #include "wx/bitmap.h"
25 #include "wx/gtk/private.h"
27 #include <gdk/gdkkeysyms.h>
29 // FIXME: is this right? somehow I don't think so (VZ)
31 #include <glib-object.h>
33 #define gtk_accel_group_attach(g, o) _gtk_accel_group_attach((g), (o))
34 #define gtk_accel_group_detach(g, o) _gtk_accel_group_detach((g), (o))
35 #define gtk_menu_ensure_uline_accel_group(m) gtk_menu_get_accel_group(m)
37 #define ACCEL_OBJECT GObject
38 #define ACCEL_OBJECTS(a) (a)->acceleratables
39 #define ACCEL_OBJ_CAST(obj) G_OBJECT(obj)
41 #define ACCEL_OBJECT GtkObject
42 #define ACCEL_OBJECTS(a) (a)->attach_objects
43 #define ACCEL_OBJ_CAST(obj) GTK_OBJECT(obj)
46 //-----------------------------------------------------------------------------
48 //-----------------------------------------------------------------------------
50 extern void wxapp_install_idle_handler();
53 #if GTK_CHECK_VERSION(1, 2, 0) && wxUSE_ACCEL
54 static wxString
GetHotKey( const wxMenuItem
& item
);
57 //-----------------------------------------------------------------------------
58 // substitute for missing GtkPixmapMenuItem
59 //-----------------------------------------------------------------------------
61 // FIXME: I can't make this compile with GTK+ 2.0, disabling for now (VZ)
63 #define USE_MENU_BITMAPS
66 #ifdef USE_MENU_BITMAPS
68 #define GTK_TYPE_PIXMAP_MENU_ITEM (gtk_pixmap_menu_item_get_type ())
69 #define GTK_PIXMAP_MENU_ITEM(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_PIXMAP_MENU_ITEM, GtkPixmapMenuItem))
70 #define GTK_PIXMAP_MENU_ITEM_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_PIXMAP_MENU_ITEM, GtkPixmapMenuItemClass))
71 #define GTK_IS_PIXMAP_MENU_ITEM(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_PIXMAP_MENU_ITEM))
72 #define GTK_IS_PIXMAP_MENU_ITEM_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PIXMAP_MENU_ITEM))
73 //#define GTK_PIXMAP_MENU_ITEM_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_PIXMAP_MENU_ITEM))
74 #define GTK_PIXMAP_MENU_ITEM_GET_CLASS(obj) (GTK_PIXMAP_MENU_ITEM_CLASS( GTK_OBJECT_GET_CLASS(obj)))
76 #ifndef GTK_MENU_ITEM_GET_CLASS
77 #define GTK_MENU_ITEM_GET_CLASS(obj) (GTK_MENU_ITEM_CLASS( GTK_OBJECT_GET_CLASS(obj)))
80 typedef struct _GtkPixmapMenuItem GtkPixmapMenuItem
;
81 typedef struct _GtkPixmapMenuItemClass GtkPixmapMenuItemClass
;
83 struct _GtkPixmapMenuItem
85 GtkMenuItem menu_item
;
90 struct _GtkPixmapMenuItemClass
92 GtkMenuItemClass parent_class
;
94 guint orig_toggle_size
;
95 guint have_pixmap_count
;
99 GtkType
gtk_pixmap_menu_item_get_type (void);
100 GtkWidget
* gtk_pixmap_menu_item_new (void);
101 void gtk_pixmap_menu_item_set_pixmap (GtkPixmapMenuItem
*menu_item
,
104 #endif // USE_MENU_BITMAPS
106 //-----------------------------------------------------------------------------
108 //-----------------------------------------------------------------------------
110 static wxString
wxReplaceUnderscore( const wxString
& title
)
114 /* GTK 1.2 wants to have "_" instead of "&" for accelerators */
117 while (*pc
!= wxT('\0'))
119 if ((*pc
== wxT('&')) && (*(pc
+1) == wxT('&')))
121 // "&" is doubled to indicate "&" instead of accelerator
125 else if (*pc
== wxT('&'))
127 #if GTK_CHECK_VERSION(1, 2, 0)
131 #if GTK_CHECK_VERSION(2, 0, 0)
132 else if (*pc
== wxT('/'))
136 else if (*pc
== wxT('\\'))
140 #elif GTK_CHECK_VERSION(1, 2, 0)
141 else if (*pc
== wxT('/'))
149 if ( *pc
== wxT('_') )
151 // underscores must be doubled to prevent them from being
152 // interpreted as accelerator character prefix by GTK
164 //-----------------------------------------------------------------------------
166 //-----------------------------------------------------------------------------
168 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar
,wxWindow
)
170 wxMenuBar::wxMenuBar( long style
)
172 /* the parent window is known after wxFrame::SetMenu() */
173 m_needParent
= FALSE
;
175 m_invokingWindow
= (wxWindow
*) NULL
;
177 if (!PreCreation( (wxWindow
*) NULL
, wxDefaultPosition
, wxDefaultSize
) ||
178 !CreateBase( (wxWindow
*) NULL
, -1, wxDefaultPosition
, wxDefaultSize
, style
, wxDefaultValidator
, wxT("menubar") ))
180 wxFAIL_MSG( wxT("wxMenuBar creation failed") );
184 m_menus
.DeleteContents( TRUE
);
186 /* GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has. */
187 #if GTK_CHECK_VERSION(1, 2, 1)
188 m_accel
= gtk_accel_group_new();
189 m_factory
= gtk_item_factory_new( GTK_TYPE_MENU_BAR
, "<main>", m_accel
);
190 m_menubar
= gtk_item_factory_get_widget( m_factory
, "<main>" );
192 m_menubar
= gtk_menu_bar_new();
195 if (style
& wxMB_DOCKABLE
)
197 m_widget
= gtk_handle_box_new();
198 gtk_container_add( GTK_CONTAINER(m_widget
), GTK_WIDGET(m_menubar
) );
199 gtk_widget_show( GTK_WIDGET(m_menubar
) );
203 m_widget
= GTK_WIDGET(m_menubar
);
211 wxMenuBar::wxMenuBar()
213 /* the parent window is known after wxFrame::SetMenu() */
214 m_needParent
= FALSE
;
216 m_invokingWindow
= (wxWindow
*) NULL
;
218 if (!PreCreation( (wxWindow
*) NULL
, wxDefaultPosition
, wxDefaultSize
) ||
219 !CreateBase( (wxWindow
*) NULL
, -1, wxDefaultPosition
, wxDefaultSize
, 0, wxDefaultValidator
, wxT("menubar") ))
221 wxFAIL_MSG( wxT("wxMenuBar creation failed") );
225 m_menus
.DeleteContents( TRUE
);
227 /* GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has. */
228 #if GTK_CHECK_VERSION(1, 2, 1)
229 m_accel
= gtk_accel_group_new();
230 m_factory
= gtk_item_factory_new( GTK_TYPE_MENU_BAR
, "<main>", m_accel
);
231 m_menubar
= gtk_item_factory_get_widget( m_factory
, "<main>" );
233 m_menubar
= gtk_menu_bar_new();
236 m_widget
= GTK_WIDGET(m_menubar
);
243 wxMenuBar::~wxMenuBar()
245 // gtk_object_unref( GTK_OBJECT(m_factory) ); why not ?
248 static void wxMenubarUnsetInvokingWindow( wxMenu
*menu
, wxWindow
*win
)
250 menu
->SetInvokingWindow( (wxWindow
*) NULL
);
252 wxWindow
*top_frame
= win
;
253 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
254 top_frame
= top_frame
->GetParent();
256 /* support for native hot keys */
257 gtk_accel_group_detach( menu
->m_accel
, ACCEL_OBJ_CAST(top_frame
->m_widget
) );
259 wxMenuItemList::Node
*node
= menu
->GetMenuItems().GetFirst();
262 wxMenuItem
*menuitem
= node
->GetData();
263 if (menuitem
->IsSubMenu())
264 wxMenubarUnsetInvokingWindow( menuitem
->GetSubMenu(), win
);
265 node
= node
->GetNext();
269 static void wxMenubarSetInvokingWindow( wxMenu
*menu
, wxWindow
*win
)
271 menu
->SetInvokingWindow( win
);
273 #if GTK_CHECK_VERSION(1, 2, 1)
274 wxWindow
*top_frame
= win
;
275 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
276 top_frame
= top_frame
->GetParent();
278 /* support for native hot keys */
279 ACCEL_OBJECT
*obj
= ACCEL_OBJ_CAST(top_frame
->m_widget
);
280 if ( !g_slist_find( ACCEL_OBJECTS(menu
->m_accel
), obj
) )
281 gtk_accel_group_attach( menu
->m_accel
, obj
);
282 #endif // GTK+ 1.2.1+
284 wxMenuItemList::Node
*node
= menu
->GetMenuItems().GetFirst();
287 wxMenuItem
*menuitem
= node
->GetData();
288 if (menuitem
->IsSubMenu())
289 wxMenubarSetInvokingWindow( menuitem
->GetSubMenu(), win
);
290 node
= node
->GetNext();
294 void wxMenuBar::SetInvokingWindow( wxWindow
*win
)
296 m_invokingWindow
= win
;
297 #if GTK_CHECK_VERSION(1, 2, 1)
298 wxWindow
*top_frame
= win
;
299 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
300 top_frame
= top_frame
->GetParent();
302 /* support for native key accelerators indicated by underscroes */
303 ACCEL_OBJECT
*obj
= ACCEL_OBJ_CAST(top_frame
->m_widget
);
304 if ( !g_slist_find( ACCEL_OBJECTS(m_accel
), obj
) )
305 gtk_accel_group_attach( m_accel
, obj
);
306 #endif // GTK+ 1.2.1+
308 wxMenuList::Node
*node
= m_menus
.GetFirst();
311 wxMenu
*menu
= node
->GetData();
312 wxMenubarSetInvokingWindow( menu
, win
);
313 node
= node
->GetNext();
317 void wxMenuBar::UnsetInvokingWindow( wxWindow
*win
)
319 m_invokingWindow
= (wxWindow
*) NULL
;
320 #if GTK_CHECK_VERSION(1, 2, 1)
321 wxWindow
*top_frame
= win
;
322 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
323 top_frame
= top_frame
->GetParent();
325 /* support for native key accelerators indicated by underscroes */
326 gtk_accel_group_detach( m_accel
, ACCEL_OBJ_CAST(top_frame
->m_widget
) );
327 #endif // GTK+ 1.2.1+
329 wxMenuList::Node
*node
= m_menus
.GetFirst();
332 wxMenu
*menu
= node
->GetData();
333 wxMenubarUnsetInvokingWindow( menu
, win
);
334 node
= node
->GetNext();
338 bool wxMenuBar::Append( wxMenu
*menu
, const wxString
&title
)
340 if ( !wxMenuBarBase::Append( menu
, title
) )
343 return GtkAppend(menu
, title
);
346 bool wxMenuBar::GtkAppend(wxMenu
*menu
, const wxString
& title
)
348 wxString
str( wxReplaceUnderscore( title
) );
350 /* this doesn't have much effect right now */
351 menu
->SetTitle( str
);
353 /* GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has. */
354 #if GTK_CHECK_VERSION(1, 2, 1)
357 buf
<< wxT('/') << str
.c_str();
359 /* local buffer in multibyte form */
361 strcpy(cbuf
, wxGTK_CONV(buf
) );
363 GtkItemFactoryEntry entry
;
364 entry
.path
= (gchar
*)cbuf
; // const_cast
365 entry
.accelerator
= (gchar
*) NULL
;
366 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
367 entry
.callback_action
= 0;
368 entry
.item_type
= (char *)"<Branch>";
370 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); /* what is 2 ? */
371 /* in order to get the pointer to the item we need the item text _without_ underscores */
372 wxString tmp
= wxT("<main>/");
374 for ( pc
= str
; *pc
!= wxT('\0'); pc
++ )
376 // contrary to the common sense, we must throw out _all_ underscores,
377 // (i.e. "Hello__World" => "HelloWorld" and not "Hello_World" as we
378 // might naively think). IMHO it's a bug in GTK+ (VZ)
379 while (*pc
== wxT('_'))
383 menu
->m_owner
= gtk_item_factory_get_item( m_factory
, wxGTK_CONV( tmp
) );
384 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu
->m_owner
), menu
->m_menu
);
387 menu
->m_owner
= gtk_menu_item_new_with_label( wxGTK_CONV( str
) );
388 gtk_widget_show( menu
->m_owner
);
389 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu
->m_owner
), menu
->m_menu
);
391 gtk_menu_bar_append( GTK_MENU_BAR(m_menubar
), menu
->m_owner
);
395 // m_invokingWindow is set after wxFrame::SetMenuBar(). This call enables
396 // addings menu later on.
397 if (m_invokingWindow
)
399 wxMenubarSetInvokingWindow( menu
, m_invokingWindow
);
401 // OPTIMISE ME: we should probably cache this, or pass it
402 // directly, but for now this is a minimal
403 // change to validate the new dynamic sizing.
404 // see (and refactor :) similar code in Remove
407 wxFrame
*frame
= wxDynamicCast( m_invokingWindow
, wxFrame
);
410 frame
->UpdateMenuBarSize();
416 bool wxMenuBar::Insert(size_t pos
, wxMenu
*menu
, const wxString
& title
)
418 if ( !wxMenuBarBase::Insert(pos
, menu
, title
) )
421 // GTK+ doesn't have a function to insert a menu using GtkItemFactory (as
422 // of version 1.2.6), so we first append the item and then change its
424 if ( !GtkAppend(menu
, title
) )
427 if (pos
+1 >= m_menus
.GetCount())
430 GtkMenuShell
*menu_shell
= GTK_MENU_SHELL(m_factory
->widget
);
431 gpointer data
= g_list_last(menu_shell
->children
)->data
;
432 menu_shell
->children
= g_list_remove(menu_shell
->children
, data
);
433 menu_shell
->children
= g_list_insert(menu_shell
->children
, data
, pos
);
438 wxMenu
*wxMenuBar::Replace(size_t pos
, wxMenu
*menu
, const wxString
& title
)
440 // remove the old item and insert a new one
441 wxMenu
*menuOld
= Remove(pos
);
442 if ( menuOld
&& !Insert(pos
, menu
, title
) )
444 return (wxMenu
*) NULL
;
447 // either Insert() succeeded or Remove() failed and menuOld is NULL
451 static wxMenu
*CopyMenu (wxMenu
*menu
)
453 wxMenu
*menucopy
= new wxMenu ();
454 wxMenuItemList::Node
*node
= menu
->GetMenuItems().GetFirst();
457 wxMenuItem
*item
= node
->GetData();
458 int itemid
= item
->GetId();
459 wxString text
= item
->GetText();
460 text
.Replace(wxT("_"), wxT("&"));
461 wxMenu
*submenu
= item
->GetSubMenu();
464 wxMenuItem
* itemcopy
= new wxMenuItem(menucopy
,
466 menu
->GetHelpString(itemid
));
467 itemcopy
->SetBitmap(item
->GetBitmap());
468 itemcopy
->SetCheckable(item
->IsCheckable());
469 menucopy
->Append(itemcopy
);
472 menucopy
->Append (itemid
, text
, CopyMenu(submenu
),
473 menu
->GetHelpString(itemid
));
475 node
= node
->GetNext();
481 wxMenu
*wxMenuBar::Remove(size_t pos
)
483 wxMenu
*menu
= wxMenuBarBase::Remove(pos
);
485 return (wxMenu
*) NULL
;
488 GtkMenuShell *menu_shell = GTK_MENU_SHELL(m_factory->widget);
490 printf( "factory entries before %d\n", (int)g_slist_length(m_factory->items) );
491 printf( "menu shell entries before %d\n", (int)g_list_length( menu_shell->children ) );
494 wxMenu
*menucopy
= CopyMenu( menu
);
496 // unparent calls unref() and that would delete the widget so we raise
497 // the ref count to 2 artificially before invoking unparent.
498 gtk_widget_ref( menu
->m_menu
);
499 gtk_widget_unparent( menu
->m_menu
);
501 gtk_widget_destroy( menu
->m_owner
);
506 printf( "factory entries after %d\n", (int)g_slist_length(m_factory->items) );
507 printf( "menu shell entries after %d\n", (int)g_list_length( menu_shell->children ) );
510 if (m_invokingWindow
)
512 // OPTIMISE ME: see comment in GtkAppend
514 wxFrame
*frame
= wxDynamicCast( m_invokingWindow
, wxFrame
);
517 frame
->UpdateMenuBarSize();
523 static int FindMenuItemRecursive( const wxMenu
*menu
, const wxString
&menuString
, const wxString
&itemString
)
525 if (wxMenuItem::GetLabelFromText(menu
->GetTitle()) == wxMenuItem::GetLabelFromText(menuString
))
527 int res
= menu
->FindItem( itemString
);
528 if (res
!= wxNOT_FOUND
)
532 wxMenuItemList::Node
*node
= menu
->GetMenuItems().GetFirst();
535 wxMenuItem
*item
= node
->GetData();
536 if (item
->IsSubMenu())
537 return FindMenuItemRecursive(item
->GetSubMenu(), menuString
, itemString
);
539 node
= node
->GetNext();
545 int wxMenuBar::FindMenuItem( const wxString
&menuString
, const wxString
&itemString
) const
547 wxMenuList::Node
*node
= m_menus
.GetFirst();
550 wxMenu
*menu
= node
->GetData();
551 int res
= FindMenuItemRecursive( menu
, menuString
, itemString
);
554 node
= node
->GetNext();
560 // Find a wxMenuItem using its id. Recurses down into sub-menus
561 static wxMenuItem
* FindMenuItemByIdRecursive(const wxMenu
* menu
, int id
)
563 wxMenuItem
* result
= menu
->FindChildItem(id
);
565 wxMenuItemList::Node
*node
= menu
->GetMenuItems().GetFirst();
566 while ( node
&& result
== NULL
)
568 wxMenuItem
*item
= node
->GetData();
569 if (item
->IsSubMenu())
571 result
= FindMenuItemByIdRecursive( item
->GetSubMenu(), id
);
573 node
= node
->GetNext();
579 wxMenuItem
* wxMenuBar::FindItem( int id
, wxMenu
**menuForItem
) const
581 wxMenuItem
* result
= 0;
582 wxMenuList::Node
*node
= m_menus
.GetFirst();
583 while (node
&& result
== 0)
585 wxMenu
*menu
= node
->GetData();
586 result
= FindMenuItemByIdRecursive( menu
, id
);
587 node
= node
->GetNext();
592 *menuForItem
= result
? result
->GetMenu() : (wxMenu
*)NULL
;
598 void wxMenuBar::EnableTop( size_t pos
, bool flag
)
600 wxMenuList::Node
*node
= m_menus
.Item( pos
);
602 wxCHECK_RET( node
, wxT("menu not found") );
604 wxMenu
* menu
= node
->GetData();
607 gtk_widget_set_sensitive( menu
->m_owner
, flag
);
610 wxString
wxMenuBar::GetLabelTop( size_t pos
) const
612 wxMenuList::Node
*node
= m_menus
.Item( pos
);
614 wxCHECK_MSG( node
, wxT("invalid"), wxT("menu not found") );
616 wxMenu
* menu
= node
->GetData();
619 wxString
text( menu
->GetTitle() );
620 for ( const wxChar
*pc
= text
.c_str(); *pc
; pc
++ )
622 if ( *pc
== wxT('_') )
624 // '_' is the escape character for GTK+
628 // don't remove ampersands '&' since if we have them in the menu title
629 // it means that they were doubled to indicate "&" instead of accelerator
637 void wxMenuBar::SetLabelTop( size_t pos
, const wxString
& label
)
639 wxMenuList::Node
*node
= m_menus
.Item( pos
);
641 wxCHECK_RET( node
, wxT("menu not found") );
643 wxMenu
* menu
= node
->GetData();
645 wxString
str( wxReplaceUnderscore( label
) );
647 menu
->SetTitle( str
);
651 GtkLabel
*label
= GTK_LABEL( GTK_BIN(menu
->m_owner
)->child
);
654 gtk_label_set( label
, wxGTK_CONV( str
) );
656 /* reparse key accel */
657 (void)gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV( str
) );
658 gtk_accel_label_refetch( GTK_ACCEL_LABEL(label
) );
663 //-----------------------------------------------------------------------------
665 //-----------------------------------------------------------------------------
667 static void gtk_menu_clicked_callback( GtkWidget
*widget
, wxMenu
*menu
)
670 wxapp_install_idle_handler();
672 int id
= menu
->FindMenuIdByMenuItem(widget
);
674 /* should find it for normal (not popup) menu */
675 wxASSERT_MSG( (id
!= -1) || (menu
->GetInvokingWindow() != NULL
),
676 _T("menu item not found in gtk_menu_clicked_callback") );
678 if (!menu
->IsEnabled(id
))
681 wxMenuItem
* item
= menu
->FindChildItem( id
);
682 wxCHECK_RET( item
, wxT("error in menu item callback") );
684 if (item
->IsCheckable())
686 bool isReallyChecked
= item
->IsChecked(),
687 isInternallyChecked
= item
->wxMenuItemBase::IsChecked();
689 // ensure that the internal state is always consistent with what is
690 // shown on the screen
691 item
->wxMenuItemBase::Check(isReallyChecked
);
693 // we must not report the events for the radio button going up nor the
694 // events resulting from the calls to wxMenuItem::Check()
695 if ( (item
->GetKind() == wxITEM_RADIO
&& !isReallyChecked
) ||
696 (isInternallyChecked
== isReallyChecked
) )
701 // the user pressed on the menu item: report the event below
704 menu
->SendEvent(id
, item
->IsCheckable() ? item
->IsChecked() : -1);
707 //-----------------------------------------------------------------------------
709 //-----------------------------------------------------------------------------
711 static void gtk_menu_hilight_callback( GtkWidget
*widget
, wxMenu
*menu
)
713 if (g_isIdle
) wxapp_install_idle_handler();
715 int id
= menu
->FindMenuIdByMenuItem(widget
);
717 wxASSERT( id
!= -1 ); // should find it!
719 if (!menu
->IsEnabled(id
))
722 wxMenuEvent
event( wxEVT_MENU_HIGHLIGHT
, id
);
723 event
.SetEventObject( menu
);
725 if (menu
->GetEventHandler()->ProcessEvent(event
))
728 wxWindow
*win
= menu
->GetInvokingWindow();
729 if (win
) win
->GetEventHandler()->ProcessEvent( event
);
732 //-----------------------------------------------------------------------------
734 //-----------------------------------------------------------------------------
736 static void gtk_menu_nolight_callback( GtkWidget
*widget
, wxMenu
*menu
)
738 if (g_isIdle
) wxapp_install_idle_handler();
740 int id
= menu
->FindMenuIdByMenuItem(widget
);
742 wxASSERT( id
!= -1 ); // should find it!
744 if (!menu
->IsEnabled(id
))
747 wxMenuEvent
event( wxEVT_MENU_HIGHLIGHT
, -1 );
748 event
.SetEventObject( menu
);
750 if (menu
->GetEventHandler()->ProcessEvent(event
))
753 wxWindow
*win
= menu
->GetInvokingWindow();
755 win
->GetEventHandler()->ProcessEvent( event
);
758 //-----------------------------------------------------------------------------
760 //-----------------------------------------------------------------------------
762 IMPLEMENT_DYNAMIC_CLASS(wxMenuItem
, wxObject
)
764 wxMenuItem
*wxMenuItemBase::New(wxMenu
*parentMenu
,
766 const wxString
& name
,
767 const wxString
& help
,
771 return new wxMenuItem(parentMenu
, id
, name
, help
, kind
, subMenu
);
774 wxMenuItem::wxMenuItem(wxMenu
*parentMenu
,
776 const wxString
& text
,
777 const wxString
& help
,
780 : wxMenuItemBase(parentMenu
, id
, text
, help
, kind
, subMenu
)
785 wxMenuItem::wxMenuItem(wxMenu
*parentMenu
,
787 const wxString
& text
,
788 const wxString
& help
,
791 : wxMenuItemBase(parentMenu
, id
, text
, help
,
792 isCheckable
? wxITEM_CHECK
: wxITEM_NORMAL
, subMenu
)
797 void wxMenuItem::Init(const wxString
& text
)
799 m_labelWidget
= (GtkWidget
*) NULL
;
800 m_menuItem
= (GtkWidget
*) NULL
;
805 wxMenuItem::~wxMenuItem()
807 // don't delete menu items, the menus take care of that
810 // return the menu item text without any menu accels
812 wxString
wxMenuItemBase::GetLabelFromText(const wxString
& text
)
816 for ( const wxChar
*pc
= text
.c_str(); *pc
; pc
++ )
818 if ( *pc
== wxT('_') )
820 // GTK 1.2 escapes "xxx_xxx" to "xxx__xxx"
826 #if GTK_CHECK_VERSION(2, 0, 0)
827 if ( *pc
== wxT('\\') )
829 // GTK 2.0 escapes "xxx/xxx" to "xxx\/xxx"
836 if ( (*pc
== wxT('&')) && (*(pc
+1) != wxT('&')) )
839 // "&" is doubled to indicate "&" instead of accelerator
848 void wxMenuItem::SetText( const wxString
& str
)
850 // Some optimization to avoid flicker
851 wxString oldLabel
= m_text
;
852 oldLabel
= wxStripMenuCodes(oldLabel
.BeforeFirst('\t'));
853 oldLabel
.Replace(wxT("_"), wxT(""));
854 wxString label1
= wxStripMenuCodes(str
.BeforeFirst('\t'));
855 if (oldLabel
== label1
)
864 label
= (GtkLabel
*) m_labelWidget
;
866 label
= GTK_LABEL( GTK_BIN(m_menuItem
)->child
);
868 #if GTK_CHECK_VERSION(2, 0, 0)
869 // We have to imitate item_factory_unescape_label here
871 for (size_t n
= 0; n
< m_text
.Len(); n
++)
873 if (m_text
[n
] != wxT('\\'))
877 gtk_label_set_text_with_mnemonic( GTK_LABEL(label
), wxGTK_CONV(tmp
) );
880 gtk_label_set( label
, wxGTK_CONV( m_text
) );
883 (void)gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV(m_text
) );
884 gtk_accel_label_refetch( GTK_ACCEL_LABEL(label
) );
889 // it's valid for this function to be called even if m_menuItem == NULL
890 void wxMenuItem::DoSetText( const wxString
& str
)
892 // '\t' is the deliminator indicating a hot key
894 const wxChar
*pc
= str
;
895 while ( (*pc
!= wxT('\0')) && (*pc
!= wxT('\t')) )
897 if ((*pc
== wxT('&')) && (*(pc
+1) == wxT('&')))
899 // "&" is doubled to indicate "&" instead of accelerator
903 else if (*pc
== wxT('&'))
907 #if GTK_CHECK_VERSION(2, 0, 0)
908 else if ( *pc
== wxT('_') ) // escape underscores
910 // m_text << wxT("__"); doesn't work
912 else if (*pc
== wxT('/')) // we have to escape slashes
914 m_text
<< wxT("\\/");
916 else if (*pc
== wxT('\\')) // we have to double backslashes
918 m_text
<< wxT("\\\\");
921 else if ( *pc
== wxT('_') ) // escape underscores
925 else if (*pc
== wxT('/')) /* we have to filter out slashes ... */
927 m_text
<< wxT('\\'); /* ... and replace them with back slashes */
947 wxAcceleratorEntry
*wxMenuItem::GetAccel() const
952 return (wxAcceleratorEntry
*)NULL
;
955 // as wxGetAccelFromString() looks for TAB, insert a dummy one here
957 label
<< wxT('\t') << GetHotKey();
959 return wxGetAccelFromString(label
);
962 #endif // wxUSE_ACCEL
964 void wxMenuItem::Check( bool check
)
966 wxCHECK_RET( m_menuItem
, wxT("invalid menu item") );
968 if (check
== m_isChecked
)
971 wxMenuItemBase::Check( check
);
977 gtk_check_menu_item_set_state( (GtkCheckMenuItem
*)m_menuItem
, (gint
)check
);
981 wxFAIL_MSG( _T("can't check this item") );
985 void wxMenuItem::Enable( bool enable
)
987 wxCHECK_RET( m_menuItem
, wxT("invalid menu item") );
989 gtk_widget_set_sensitive( m_menuItem
, enable
);
990 wxMenuItemBase::Enable( enable
);
993 bool wxMenuItem::IsChecked() const
995 wxCHECK_MSG( m_menuItem
, FALSE
, wxT("invalid menu item") );
997 wxCHECK_MSG( IsCheckable(), FALSE
,
998 wxT("can't get state of uncheckable item!") );
1000 return ((GtkCheckMenuItem
*)m_menuItem
)->active
!= 0;
1003 wxString
wxMenuItem::GetFactoryPath() const
1005 /* in order to get the pointer to the item we need the item text
1006 _without_ underscores */
1007 wxString
path( wxT("<main>/") );
1009 for ( const wxChar
*pc
= m_text
.c_str(); *pc
; pc
++ )
1011 if ( *pc
== wxT('_') )
1013 // remove '_' unconditionally
1017 // don't remove ampersands '&' since if we have them in the menu item title
1018 // it means that they were doubled to indicate "&" instead of accelerator
1026 //-----------------------------------------------------------------------------
1028 //-----------------------------------------------------------------------------
1030 IMPLEMENT_DYNAMIC_CLASS(wxMenu
,wxEvtHandler
)
1034 m_accel
= gtk_accel_group_new();
1035 m_factory
= gtk_item_factory_new( GTK_TYPE_MENU
, "<main>", m_accel
);
1036 m_menu
= gtk_item_factory_get_widget( m_factory
, "<main>" );
1038 m_owner
= (GtkWidget
*) NULL
;
1040 /* Tearoffs are entries, just like separators. So if we want this
1041 menu to be a tear-off one, we just append a tearoff entry
1043 if(m_style
& wxMENU_TEAROFF
)
1045 GtkItemFactoryEntry entry
;
1046 entry
.path
= (char *)"/tearoff";
1047 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
1048 entry
.callback_action
= 0;
1049 entry
.item_type
= (char *)"<Tearoff>";
1050 entry
.accelerator
= (gchar
*) NULL
;
1051 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); /* what is 2 ? */
1052 //GtkWidget *menuItem = gtk_item_factory_get_widget( m_factory, "<main>/tearoff" );
1055 // append the title as the very first entry if we have it
1058 Append(-2, m_title
);
1067 gtk_widget_destroy( m_menu
);
1069 gtk_object_unref( GTK_OBJECT(m_factory
) );
1072 bool wxMenu::GtkAppend(wxMenuItem
*mitem
)
1074 GtkWidget
*menuItem
;
1076 #if defined(USE_MENU_BITMAPS) || !GTK_CHECK_VERSION(1, 2, 0)
1077 bool appended
= FALSE
;
1080 // does this item terminate the current radio group?
1081 bool endOfRadioGroup
= TRUE
;
1083 if ( mitem
->IsSeparator() )
1085 GtkItemFactoryEntry entry
;
1086 entry
.path
= (char *)"/sep";
1087 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
1088 entry
.callback_action
= 0;
1089 entry
.item_type
= (char *)"<Separator>";
1090 entry
.accelerator
= (gchar
*) NULL
;
1092 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); /* what is 2 ? */
1094 /* this will be wrong for more than one separator. do we care? */
1095 menuItem
= gtk_item_factory_get_widget( m_factory
, "<main>/sep" );
1097 // we might have a separator inside a radio group
1098 endOfRadioGroup
= FALSE
;
1100 else if ( mitem
->IsSubMenu() )
1102 /* text has "_" instead of "&" after mitem->SetText() */
1103 wxString
text( mitem
->GetText() );
1105 /* local buffer in multibyte form */
1108 strcat( buf
, wxGTK_CONV( text
) );
1110 GtkItemFactoryEntry entry
;
1112 entry
.callback
= (GtkItemFactoryCallback
) 0;
1113 entry
.callback_action
= 0;
1114 entry
.item_type
= (char *)"<Branch>";
1115 entry
.accelerator
= (gchar
*) NULL
;
1117 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); /* what is 2 ? */
1119 wxString
path( mitem
->GetFactoryPath() );
1120 menuItem
= gtk_item_factory_get_item( m_factory
, wxGTK_CONV( path
) );
1122 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menuItem
), mitem
->GetSubMenu()->m_menu
);
1124 // if adding a submenu to a menu already existing in the menu bar, we
1125 // must set invoking window to allow processing events from this
1127 if ( m_invokingWindow
)
1128 wxMenubarSetInvokingWindow(mitem
->GetSubMenu(), m_invokingWindow
);
1130 #ifdef USE_MENU_BITMAPS
1131 else if (mitem
->GetBitmap().Ok()) // An item with bitmap
1133 wxString
text( mitem
->GetText() );
1134 const wxBitmap
*bitmap
= &mitem
->GetBitmap();
1136 menuItem
= gtk_pixmap_menu_item_new ();
1137 GtkWidget
*label
= gtk_accel_label_new ( wxGTK_CONV( text
) );
1138 gtk_misc_set_alignment (GTK_MISC (label
), 0.0, 0.5);
1139 gtk_container_add (GTK_CONTAINER (menuItem
), label
);
1140 guint accel_key
= gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV( text
) );
1141 gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (label
), menuItem
);
1142 if (accel_key
!= GDK_VoidSymbol
)
1144 gtk_widget_add_accelerator (menuItem
,
1146 gtk_menu_ensure_uline_accel_group (GTK_MENU (m_menu
)),
1150 gtk_widget_show (label
);
1152 mitem
->SetLabelWidget(label
);
1154 GtkWidget
* pixmap
= gtk_pixmap_new( bitmap
->GetPixmap(), bitmap
->GetMask() ? bitmap
->GetMask()->GetBitmap() : (GdkBitmap
* )NULL
);
1155 gtk_widget_show(pixmap
);
1156 gtk_pixmap_menu_item_set_pixmap(GTK_PIXMAP_MENU_ITEM( menuItem
), pixmap
);
1158 gtk_signal_connect( GTK_OBJECT(menuItem
), "activate",
1159 GTK_SIGNAL_FUNC(gtk_menu_clicked_callback
),
1162 gtk_menu_append( GTK_MENU(m_menu
), menuItem
);
1163 gtk_widget_show( menuItem
);
1165 appended
= TRUE
; // We've done this, don't do it again
1167 #endif // USE_MENU_BITMAPS
1168 else // a normal item
1170 // text has "_" instead of "&" after mitem->SetText() so don't use it
1171 wxString
text( mitem
->GetText() );
1173 // buffers containing the menu item path and type in multibyte form
1177 strcpy( bufPath
, "/" );
1178 strncat( bufPath
, wxGTK_CONV(text
), WXSIZEOF(bufPath
) - 2 );
1179 bufPath
[WXSIZEOF(bufPath
) - 1] = '\0';
1181 GtkItemFactoryEntry entry
;
1182 entry
.path
= bufPath
;
1183 entry
.callback
= (GtkItemFactoryCallback
) gtk_menu_clicked_callback
;
1184 entry
.callback_action
= 0;
1187 const char *item_type
;
1188 switch ( mitem
->GetKind() )
1191 item_type
= "<CheckItem>";
1195 if ( m_pathLastRadio
.empty() )
1197 // start of a new radio group
1198 item_type
= "<RadioItem>";
1199 wxString
tmp( wxGTK_CONV_BACK( bufPath
) );
1201 m_pathLastRadio
= tmp
;
1203 else // continue the radio group
1205 pathRadio
= m_pathLastRadio
;
1206 pathRadio
.Replace(wxT("_"), wxT(""));
1207 pathRadio
.Prepend(wxT("<main>/"));
1209 strncpy(bufType
, wxGTK_CONV(pathRadio
), WXSIZEOF(bufType
));
1210 bufType
[WXSIZEOF(bufType
) - 1] = '\0';
1211 item_type
= bufType
;
1214 // continue the existing radio group, if any
1215 endOfRadioGroup
= FALSE
;
1219 wxFAIL_MSG( _T("unexpected menu item kind") );
1223 item_type
= "<Item>";
1227 entry
.item_type
= (char *)item_type
; // cast needed for GTK+
1228 entry
.accelerator
= (gchar
*) NULL
;
1231 // due to an apparent bug in GTK+, we have to use a static buffer here -
1232 // otherwise GTK+ 1.2.2 manages to override the memory we pass to it
1234 char s_accel
[50]; // should be big enough, we check for overruns
1235 wxString
tmp( GetHotKey(*mitem
) );
1236 strncpy(s_accel
, wxGTK_CONV( tmp
), WXSIZEOF(s_accel
));
1237 s_accel
[WXSIZEOF(s_accel
) - 1] = '\0';
1238 entry
.accelerator
= s_accel
;
1239 #else // !wxUSE_ACCEL
1240 entry
.accelerator
= (char*) NULL
;
1241 #endif // wxUSE_ACCEL/!wxUSE_ACCEL
1243 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); /* what is 2 ? */
1245 wxString
path( mitem
->GetFactoryPath() );
1246 menuItem
= gtk_item_factory_get_widget( m_factory
, wxGTK_CONV( path
) );
1249 wxLogError( wxT("Wrong menu path: %s\n"), path
.c_str() );
1252 if ( !mitem
->IsSeparator() )
1254 wxASSERT_MSG( menuItem
, wxT("invalid menuitem") );
1256 gtk_signal_connect( GTK_OBJECT(menuItem
), "select",
1257 GTK_SIGNAL_FUNC(gtk_menu_hilight_callback
),
1260 gtk_signal_connect( GTK_OBJECT(menuItem
), "deselect",
1261 GTK_SIGNAL_FUNC(gtk_menu_nolight_callback
),
1265 mitem
->SetMenuItem(menuItem
);
1267 if ( endOfRadioGroup
)
1269 m_pathLastRadio
.clear();
1275 bool wxMenu::DoAppend(wxMenuItem
*mitem
)
1277 return GtkAppend(mitem
) && wxMenuBase::DoAppend(mitem
);
1280 bool wxMenu::DoInsert(size_t pos
, wxMenuItem
*item
)
1282 if ( !wxMenuBase::DoInsert(pos
, item
) )
1286 // GTK+ doesn't have a function to insert a menu using GtkItemFactory (as
1287 // of version 1.2.6), so we first append the item and then change its
1289 if ( !GtkAppend(item
) )
1292 if ( m_style
& wxMENU_TEAROFF
)
1294 // change the position as the first item is the tear-off marker
1298 GtkMenuShell
*menu_shell
= GTK_MENU_SHELL(m_factory
->widget
);
1299 gpointer data
= g_list_last(menu_shell
->children
)->data
;
1300 menu_shell
->children
= g_list_remove(menu_shell
->children
, data
);
1301 menu_shell
->children
= g_list_insert(menu_shell
->children
, data
, pos
);
1305 // this should be easy to do...
1306 wxFAIL_MSG( wxT("not implemented") );
1309 #endif // GTK 1.2/1.0
1312 wxMenuItem
*wxMenu::DoRemove(wxMenuItem
*item
)
1314 if ( !wxMenuBase::DoRemove(item
) )
1315 return (wxMenuItem
*)NULL
;
1317 // TODO: this code doesn't delete the item factory item and this seems
1318 // impossible as of GTK 1.2.6.
1319 gtk_widget_destroy( item
->GetMenuItem() );
1324 int wxMenu::FindMenuIdByMenuItem( GtkWidget
*menuItem
) const
1326 wxNode
*node
= m_items
.First();
1329 wxMenuItem
*item
= (wxMenuItem
*)node
->Data();
1330 if (item
->GetMenuItem() == menuItem
)
1331 return item
->GetId();
1332 node
= node
->Next();
1338 // ----------------------------------------------------------------------------
1340 // ----------------------------------------------------------------------------
1342 #if GTK_CHECK_VERSION(1, 2, 0) && wxUSE_ACCEL
1344 static wxString
GetHotKey( const wxMenuItem
& item
)
1348 wxAcceleratorEntry
*accel
= item
.GetAccel();
1351 int flags
= accel
->GetFlags();
1352 if ( flags
& wxACCEL_ALT
)
1353 hotkey
+= wxT("<alt>");
1354 if ( flags
& wxACCEL_CTRL
)
1355 hotkey
+= wxT("<control>");
1356 if ( flags
& wxACCEL_SHIFT
)
1357 hotkey
+= wxT("<shift>");
1359 int code
= accel
->GetKeyCode();
1374 hotkey
<< wxT('F') << code
- WXK_F1
+ 1;
1377 // TODO: we should use gdk_keyval_name() (a.k.a.
1378 // XKeysymToString) here as well as hardcoding the keysym
1379 // names this might be not portable
1380 case WXK_NUMPAD_INSERT
:
1381 hotkey
<< wxT("KP_Insert" );
1383 case WXK_NUMPAD_DELETE
:
1384 hotkey
<< wxT("KP_Delete" );
1387 hotkey
<< wxT("Insert" );
1390 hotkey
<< wxT("Delete" );
1393 hotkey
<< wxT("Up" );
1396 hotkey
<< wxT("Down" );
1399 hotkey
<< wxT("Prior" );
1402 hotkey
<< wxT("Next" );
1405 hotkey
<< wxT("Left" );
1408 hotkey
<< wxT("Right" );
1411 hotkey
<< wxT("Home" );
1414 hotkey
<< wxT("End" );
1417 hotkey
<< wxT("Return" );
1420 // if there are any other keys wxGetAccelFromString() may
1421 // return, we should process them here
1426 wxString name
= wxGTK_CONV_BACK( gdk_keyval_name((guint
)code
) );
1434 wxFAIL_MSG( wxT("unknown keyboard accel") );
1443 #endif // wxUSE_ACCEL
1446 //-----------------------------------------------------------------------------
1447 // substitute for missing GtkPixmapMenuItem
1448 //-----------------------------------------------------------------------------
1450 #ifdef USE_MENU_BITMAPS
1453 * Copyright (C) 1998, 1999, 2000 Free Software Foundation
1454 * All rights reserved.
1456 * This file is part of the Gnome Library.
1458 * The Gnome Library is free software; you can redistribute it and/or
1459 * modify it under the terms of the GNU Library General Public License as
1460 * published by the Free Software Foundation; either version 2 of the
1461 * License, or (at your option) any later version.
1463 * The Gnome Library is distributed in the hope that it will be useful,
1464 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1465 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1466 * Library General Public License for more details.
1468 * You should have received a copy of the GNU Library General Public
1469 * License along with the Gnome Library; see the file COPYING.LIB. If not,
1470 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
1471 * Boston, MA 02111-1307, USA.
1477 /* Author: Dietmar Maurer <dm@vlsivie.tuwien.ac.at> */
1479 #include <gtk/gtkaccellabel.h>
1480 #include <gtk/gtksignal.h>
1481 #include <gtk/gtkmenuitem.h>
1482 #include <gtk/gtkmenu.h>
1483 #include <gtk/gtkcontainer.h>
1488 static void gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass
*klass
);
1489 static void gtk_pixmap_menu_item_init (GtkPixmapMenuItem
*menu_item
);
1490 static void gtk_pixmap_menu_item_draw (GtkWidget
*widget
,
1491 GdkRectangle
*area
);
1492 static gint
gtk_pixmap_menu_item_expose (GtkWidget
*widget
,
1493 GdkEventExpose
*event
);
1495 /* we must override the following functions */
1497 static void gtk_pixmap_menu_item_map (GtkWidget
*widget
);
1498 static void gtk_pixmap_menu_item_size_allocate (GtkWidget
*widget
,
1499 GtkAllocation
*allocation
);
1500 static void gtk_pixmap_menu_item_forall (GtkContainer
*container
,
1501 gboolean include_internals
,
1502 GtkCallback callback
,
1503 gpointer callback_data
);
1504 static void gtk_pixmap_menu_item_size_request (GtkWidget
*widget
,
1505 GtkRequisition
*requisition
);
1506 static void gtk_pixmap_menu_item_remove (GtkContainer
*container
,
1509 static void changed_have_pixmap_status (GtkPixmapMenuItem
*menu_item
);
1511 static GtkMenuItemClass
*parent_class
= NULL
;
1515 #define BORDER_SPACING 3
1516 #define PMAP_WIDTH 20
1519 gtk_pixmap_menu_item_get_type (void)
1521 static GtkType pixmap_menu_item_type
= 0;
1523 if (!pixmap_menu_item_type
)
1525 GtkTypeInfo pixmap_menu_item_info
=
1527 (char *)"GtkPixmapMenuItem",
1528 sizeof (GtkPixmapMenuItem
),
1529 sizeof (GtkPixmapMenuItemClass
),
1530 (GtkClassInitFunc
) gtk_pixmap_menu_item_class_init
,
1531 (GtkObjectInitFunc
) gtk_pixmap_menu_item_init
,
1532 /* reserved_1 */ NULL
,
1533 /* reserved_2 */ NULL
,
1534 (GtkClassInitFunc
) NULL
,
1537 pixmap_menu_item_type
= gtk_type_unique (gtk_menu_item_get_type (),
1538 &pixmap_menu_item_info
);
1541 return pixmap_menu_item_type
;
1545 * gtk_pixmap_menu_item_new
1547 * Creates a new pixmap menu item. Use gtk_pixmap_menu_item_set_pixmap()
1548 * to set the pixmap wich is displayed at the left side.
1551 * &GtkWidget pointer to new menu item
1555 gtk_pixmap_menu_item_new (void)
1557 return GTK_WIDGET (gtk_type_new (gtk_pixmap_menu_item_get_type ()));
1561 gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass
*klass
)
1563 GtkObjectClass
*object_class
;
1564 GtkWidgetClass
*widget_class
;
1565 GtkMenuItemClass
*menu_item_class
;
1566 GtkContainerClass
*container_class
;
1568 object_class
= (GtkObjectClass
*) klass
;
1569 widget_class
= (GtkWidgetClass
*) klass
;
1570 menu_item_class
= (GtkMenuItemClass
*) klass
;
1571 container_class
= (GtkContainerClass
*) klass
;
1573 parent_class
= (GtkMenuItemClass
*) gtk_type_class (gtk_menu_item_get_type ());
1575 widget_class
->draw
= gtk_pixmap_menu_item_draw
;
1576 widget_class
->expose_event
= gtk_pixmap_menu_item_expose
;
1577 widget_class
->map
= gtk_pixmap_menu_item_map
;
1578 widget_class
->size_allocate
= gtk_pixmap_menu_item_size_allocate
;
1579 widget_class
->size_request
= gtk_pixmap_menu_item_size_request
;
1581 container_class
->forall
= gtk_pixmap_menu_item_forall
;
1582 container_class
->remove
= gtk_pixmap_menu_item_remove
;
1584 klass
->orig_toggle_size
= menu_item_class
->toggle_size
;
1585 klass
->have_pixmap_count
= 0;
1589 gtk_pixmap_menu_item_init (GtkPixmapMenuItem
*menu_item
)
1593 mi
= GTK_MENU_ITEM (menu_item
);
1595 menu_item
->pixmap
= NULL
;
1599 gtk_pixmap_menu_item_draw (GtkWidget
*widget
,
1602 g_return_if_fail (widget
!= NULL
);
1603 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
));
1604 g_return_if_fail (area
!= NULL
);
1606 if (GTK_WIDGET_CLASS (parent_class
)->draw
)
1607 (* GTK_WIDGET_CLASS (parent_class
)->draw
) (widget
, area
);
1609 if (GTK_WIDGET_DRAWABLE (widget
) &&
1610 GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
) {
1611 gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
),NULL
);
1616 gtk_pixmap_menu_item_expose (GtkWidget
*widget
,
1617 GdkEventExpose
*event
)
1619 g_return_val_if_fail (widget
!= NULL
, FALSE
);
1620 g_return_val_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
), FALSE
);
1621 g_return_val_if_fail (event
!= NULL
, FALSE
);
1623 if (GTK_WIDGET_CLASS (parent_class
)->expose_event
)
1624 (* GTK_WIDGET_CLASS (parent_class
)->expose_event
) (widget
, event
);
1626 if (GTK_WIDGET_DRAWABLE (widget
) &&
1627 GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
) {
1628 gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
),NULL
);
1635 * gtk_pixmap_menu_item_set_pixmap
1636 * @menu_item: Pointer to the pixmap menu item
1637 * @pixmap: Pointer to a pixmap widget
1639 * Set the pixmap of the menu item.
1644 gtk_pixmap_menu_item_set_pixmap (GtkPixmapMenuItem
*menu_item
,
1647 g_return_if_fail (menu_item
!= NULL
);
1648 g_return_if_fail (pixmap
!= NULL
);
1649 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (menu_item
));
1650 g_return_if_fail (GTK_IS_WIDGET (pixmap
));
1651 g_return_if_fail (menu_item
->pixmap
== NULL
);
1653 gtk_widget_set_parent (pixmap
, GTK_WIDGET (menu_item
));
1654 menu_item
->pixmap
= pixmap
;
1656 if (GTK_WIDGET_REALIZED (pixmap
->parent
) &&
1657 !GTK_WIDGET_REALIZED (pixmap
))
1658 gtk_widget_realize (pixmap
);
1660 if (GTK_WIDGET_VISIBLE (pixmap
->parent
)) {
1661 if (GTK_WIDGET_MAPPED (pixmap
->parent
) &&
1662 GTK_WIDGET_VISIBLE(pixmap
) &&
1663 !GTK_WIDGET_MAPPED (pixmap
))
1664 gtk_widget_map (pixmap
);
1667 changed_have_pixmap_status(menu_item
);
1669 if (GTK_WIDGET_VISIBLE (pixmap
) && GTK_WIDGET_VISIBLE (menu_item
))
1670 gtk_widget_queue_resize (pixmap
);
1674 gtk_pixmap_menu_item_map (GtkWidget
*widget
)
1676 GtkPixmapMenuItem
*menu_item
;
1678 g_return_if_fail (widget
!= NULL
);
1679 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
));
1681 menu_item
= GTK_PIXMAP_MENU_ITEM(widget
);
1683 GTK_WIDGET_CLASS(parent_class
)->map(widget
);
1685 if (menu_item
->pixmap
&&
1686 GTK_WIDGET_VISIBLE (menu_item
->pixmap
) &&
1687 !GTK_WIDGET_MAPPED (menu_item
->pixmap
))
1688 gtk_widget_map (menu_item
->pixmap
);
1692 gtk_pixmap_menu_item_size_allocate (GtkWidget
*widget
,
1693 GtkAllocation
*allocation
)
1695 GtkPixmapMenuItem
*pmenu_item
;
1697 pmenu_item
= GTK_PIXMAP_MENU_ITEM(widget
);
1699 if (pmenu_item
->pixmap
&& GTK_WIDGET_VISIBLE(pmenu_item
))
1701 GtkAllocation child_allocation
;
1704 border_width
= GTK_CONTAINER (widget
)->border_width
;
1706 child_allocation
.width
= pmenu_item
->pixmap
->requisition
.width
;
1707 child_allocation
.height
= pmenu_item
->pixmap
->requisition
.height
;
1708 child_allocation
.x
= border_width
+ BORDER_SPACING
;
1709 child_allocation
.y
= (border_width
+ BORDER_SPACING
1710 + (((allocation
->height
- child_allocation
.height
) - child_allocation
.x
)
1711 / 2)); /* center pixmaps vertically */
1712 gtk_widget_size_allocate (pmenu_item
->pixmap
, &child_allocation
);
1715 if (GTK_WIDGET_CLASS (parent_class
)->size_allocate
)
1716 GTK_WIDGET_CLASS(parent_class
)->size_allocate (widget
, allocation
);
1720 gtk_pixmap_menu_item_forall (GtkContainer
*container
,
1721 gboolean include_internals
,
1722 GtkCallback callback
,
1723 gpointer callback_data
)
1725 GtkPixmapMenuItem
*menu_item
;
1727 g_return_if_fail (container
!= NULL
);
1728 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container
));
1729 g_return_if_fail (callback
!= NULL
);
1731 menu_item
= GTK_PIXMAP_MENU_ITEM (container
);
1733 if (menu_item
->pixmap
)
1734 (* callback
) (menu_item
->pixmap
, callback_data
);
1736 GTK_CONTAINER_CLASS(parent_class
)->forall(container
,include_internals
,
1737 callback
,callback_data
);
1741 gtk_pixmap_menu_item_size_request (GtkWidget
*widget
,
1742 GtkRequisition
*requisition
)
1744 GtkPixmapMenuItem
*menu_item
;
1745 GtkRequisition req
= {0, 0};
1747 g_return_if_fail (widget
!= NULL
);
1748 g_return_if_fail (GTK_IS_MENU_ITEM (widget
));
1749 g_return_if_fail (requisition
!= NULL
);
1751 GTK_WIDGET_CLASS(parent_class
)->size_request(widget
,requisition
);
1753 menu_item
= GTK_PIXMAP_MENU_ITEM (widget
);
1755 if (menu_item
->pixmap
)
1756 gtk_widget_size_request(menu_item
->pixmap
, &req
);
1758 requisition
->height
= MAX(req
.height
+ GTK_CONTAINER(widget
)->border_width
+ BORDER_SPACING
, (unsigned int) requisition
->height
);
1759 requisition
->width
+= (req
.width
+ GTK_CONTAINER(widget
)->border_width
+ BORDER_SPACING
);
1763 gtk_pixmap_menu_item_remove (GtkContainer
*container
,
1767 gboolean widget_was_visible
;
1769 g_return_if_fail (container
!= NULL
);
1770 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container
));
1771 g_return_if_fail (child
!= NULL
);
1772 g_return_if_fail (GTK_IS_WIDGET (child
));
1774 bin
= GTK_BIN (container
);
1775 g_return_if_fail ((bin
->child
== child
||
1776 (GTK_PIXMAP_MENU_ITEM(container
)->pixmap
== child
)));
1778 widget_was_visible
= GTK_WIDGET_VISIBLE (child
);
1780 gtk_widget_unparent (child
);
1781 if (bin
->child
== child
)
1784 GTK_PIXMAP_MENU_ITEM(container
)->pixmap
= NULL
;
1785 changed_have_pixmap_status(GTK_PIXMAP_MENU_ITEM(container
));
1788 if (widget_was_visible
)
1789 gtk_widget_queue_resize (GTK_WIDGET (container
));
1793 /* important to only call this if there was actually a _change_ in pixmap == NULL */
1795 changed_have_pixmap_status (GtkPixmapMenuItem
*menu_item
)
1797 if (menu_item
->pixmap
!= NULL
) {
1798 GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
+= 1;
1800 if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
== 1) {
1801 /* Install pixmap toggle size */
1802 GTK_MENU_ITEM_GET_CLASS(menu_item
)->toggle_size
= MAX(GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->orig_toggle_size
, PMAP_WIDTH
);
1805 GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
-= 1;
1807 if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
== 0) {
1808 /* Install normal toggle size */
1809 GTK_MENU_ITEM_GET_CLASS(menu_item
)->toggle_size
= GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->orig_toggle_size
;
1813 /* Note that we actually need to do this for _all_ GtkPixmapMenuItem
1814 whenever the klass->toggle_size changes; but by doing it anytime
1815 this function is called, we get the same effect, just because of
1816 how the preferences option to show pixmaps works. Bogus, broken.
1818 if (GTK_WIDGET_VISIBLE(GTK_WIDGET(menu_item
)))
1819 gtk_widget_queue_resize(GTK_WIDGET(menu_item
));
1822 #endif // USE_MENU_BITMAPS