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
846 // wxPrintf( L"text %s label %s\n", text.c_str(), label.c_str() );
851 void wxMenuItem::SetText( const wxString
& str
)
853 // Some optimization to avoid flicker
854 wxString oldLabel
= m_text
;
855 oldLabel
= wxStripMenuCodes(oldLabel
.BeforeFirst('\t'));
856 oldLabel
.Replace(wxT("_"), wxT(""));
857 wxString label1
= wxStripMenuCodes(str
.BeforeFirst('\t'));
858 if (oldLabel
== label1
)
867 label
= (GtkLabel
*) m_labelWidget
;
869 label
= GTK_LABEL( GTK_BIN(m_menuItem
)->child
);
871 #if GTK_CHECK_VERSION(2, 0, 0)
872 // We have to imitate item_factory_unescape_label here
874 for (size_t n
= 0; n
< m_text
.Len(); n
++)
876 if (m_text
[n
] != wxT('\\'))
880 gtk_label_set_text_with_mnemonic( GTK_LABEL(label
), wxGTK_CONV(tmp
) );
883 gtk_label_set( label
, wxGTK_CONV( m_text
) );
886 (void)gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV(m_text
) );
887 gtk_accel_label_refetch( GTK_ACCEL_LABEL(label
) );
892 // it's valid for this function to be called even if m_menuItem == NULL
893 void wxMenuItem::DoSetText( const wxString
& str
)
895 // '\t' is the deliminator indicating a hot key
897 const wxChar
*pc
= str
;
898 while ( (*pc
!= wxT('\0')) && (*pc
!= wxT('\t')) )
900 if ((*pc
== wxT('&')) && (*(pc
+1) == wxT('&')))
902 // "&" is doubled to indicate "&" instead of accelerator
906 else if (*pc
== wxT('&'))
910 #if GTK_CHECK_VERSION(2, 0, 0)
911 else if ( *pc
== wxT('_') ) // escape underscores
913 // m_text << wxT("__"); doesn't work
916 else if (*pc
== wxT('/')) // we have to escape slashes
918 m_text
<< wxT("\\/");
920 else if (*pc
== wxT('\\')) // we have to double backslashes
922 m_text
<< wxT("\\\\");
925 else if ( *pc
== wxT('_') ) // escape underscores
929 else if (*pc
== wxT('/')) /* we have to filter out slashes ... */
931 m_text
<< wxT('\\'); /* ... and replace them with back slashes */
940 // wxPrintf( L"str %s m_text %s\n", str.c_str(), m_text.c_str() );
953 wxAcceleratorEntry
*wxMenuItem::GetAccel() const
958 return (wxAcceleratorEntry
*)NULL
;
961 // as wxGetAccelFromString() looks for TAB, insert a dummy one here
963 label
<< wxT('\t') << GetHotKey();
965 return wxGetAccelFromString(label
);
968 #endif // wxUSE_ACCEL
970 void wxMenuItem::Check( bool check
)
972 wxCHECK_RET( m_menuItem
, wxT("invalid menu item") );
974 if (check
== m_isChecked
)
977 wxMenuItemBase::Check( check
);
983 gtk_check_menu_item_set_state( (GtkCheckMenuItem
*)m_menuItem
, (gint
)check
);
987 wxFAIL_MSG( _T("can't check this item") );
991 void wxMenuItem::Enable( bool enable
)
993 wxCHECK_RET( m_menuItem
, wxT("invalid menu item") );
995 gtk_widget_set_sensitive( m_menuItem
, enable
);
996 wxMenuItemBase::Enable( enable
);
999 bool wxMenuItem::IsChecked() const
1001 wxCHECK_MSG( m_menuItem
, FALSE
, wxT("invalid menu item") );
1003 wxCHECK_MSG( IsCheckable(), FALSE
,
1004 wxT("can't get state of uncheckable item!") );
1006 return ((GtkCheckMenuItem
*)m_menuItem
)->active
!= 0;
1009 wxString
wxMenuItem::GetFactoryPath() const
1011 // In order to get the pointer to the item we need the item
1012 // text _without_ underscores in GTK 1.2
1013 wxString
path( wxT("<main>/") );
1015 for ( const wxChar
*pc
= m_text
.c_str(); *pc
; pc
++ )
1017 if ( *pc
== wxT('_') )
1022 // remove '_' unconditionally
1027 // don't remove ampersands '&' since if we have them in the menu item title
1028 // it means that they were doubled to indicate "&" instead of accelerator
1036 //-----------------------------------------------------------------------------
1038 //-----------------------------------------------------------------------------
1040 IMPLEMENT_DYNAMIC_CLASS(wxMenu
,wxEvtHandler
)
1044 m_accel
= gtk_accel_group_new();
1045 m_factory
= gtk_item_factory_new( GTK_TYPE_MENU
, "<main>", m_accel
);
1046 m_menu
= gtk_item_factory_get_widget( m_factory
, "<main>" );
1048 m_owner
= (GtkWidget
*) NULL
;
1050 // Tearoffs are entries, just like separators. So if we want this
1051 // menu to be a tear-off one, we just append a tearoff entry
1053 if(m_style
& wxMENU_TEAROFF
)
1055 GtkItemFactoryEntry entry
;
1056 entry
.path
= (char *)"/tearoff";
1057 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
1058 entry
.callback_action
= 0;
1059 entry
.item_type
= (char *)"<Tearoff>";
1060 entry
.accelerator
= (gchar
*) NULL
;
1061 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
1062 //GtkWidget *menuItem = gtk_item_factory_get_widget( m_factory, "<main>/tearoff" );
1065 // append the title as the very first entry if we have it
1068 Append(-2, m_title
);
1077 gtk_widget_destroy( m_menu
);
1079 gtk_object_unref( GTK_OBJECT(m_factory
) );
1082 bool wxMenu::GtkAppend(wxMenuItem
*mitem
)
1084 GtkWidget
*menuItem
;
1086 #if defined(USE_MENU_BITMAPS) || !GTK_CHECK_VERSION(1, 2, 0)
1087 bool appended
= FALSE
;
1090 // does this item terminate the current radio group?
1091 bool endOfRadioGroup
= TRUE
;
1093 if ( mitem
->IsSeparator() )
1095 GtkItemFactoryEntry entry
;
1096 entry
.path
= (char *)"/sep";
1097 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
1098 entry
.callback_action
= 0;
1099 entry
.item_type
= (char *)"<Separator>";
1100 entry
.accelerator
= (gchar
*) NULL
;
1102 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
1104 // this will be wrong for more than one separator. do we care?
1105 menuItem
= gtk_item_factory_get_widget( m_factory
, "<main>/sep" );
1107 // we might have a separator inside a radio group
1108 endOfRadioGroup
= FALSE
;
1110 else if ( mitem
->IsSubMenu() )
1112 // text has "_" instead of "&" after mitem->SetText()
1113 wxString
text( mitem
->GetText() );
1115 // local buffer in multibyte form
1118 strcat( buf
, wxGTK_CONV( text
) );
1120 GtkItemFactoryEntry entry
;
1122 entry
.callback
= (GtkItemFactoryCallback
) 0;
1123 entry
.callback_action
= 0;
1124 entry
.item_type
= (char *)"<Branch>";
1125 entry
.accelerator
= (gchar
*) NULL
;
1127 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
1129 wxString
path( mitem
->GetFactoryPath() );
1130 menuItem
= gtk_item_factory_get_item( m_factory
, wxGTK_CONV( path
) );
1132 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menuItem
), mitem
->GetSubMenu()->m_menu
);
1134 // if adding a submenu to a menu already existing in the menu bar, we
1135 // must set invoking window to allow processing events from this
1137 if ( m_invokingWindow
)
1138 wxMenubarSetInvokingWindow(mitem
->GetSubMenu(), m_invokingWindow
);
1140 #ifdef USE_MENU_BITMAPS
1141 else if (mitem
->GetBitmap().Ok()) // An item with bitmap
1143 wxString
text( mitem
->GetText() );
1144 const wxBitmap
*bitmap
= &mitem
->GetBitmap();
1146 menuItem
= gtk_pixmap_menu_item_new ();
1147 GtkWidget
*label
= gtk_accel_label_new ( wxGTK_CONV( text
) );
1148 gtk_misc_set_alignment (GTK_MISC (label
), 0.0, 0.5);
1149 gtk_container_add (GTK_CONTAINER (menuItem
), label
);
1150 guint accel_key
= gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV( text
) );
1151 gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (label
), menuItem
);
1152 if (accel_key
!= GDK_VoidSymbol
)
1154 gtk_widget_add_accelerator (menuItem
,
1156 gtk_menu_ensure_uline_accel_group (GTK_MENU (m_menu
)),
1160 gtk_widget_show (label
);
1162 mitem
->SetLabelWidget(label
);
1164 GtkWidget
* pixmap
= gtk_pixmap_new( bitmap
->GetPixmap(), bitmap
->GetMask() ? bitmap
->GetMask()->GetBitmap() : (GdkBitmap
* )NULL
);
1165 gtk_widget_show(pixmap
);
1166 gtk_pixmap_menu_item_set_pixmap(GTK_PIXMAP_MENU_ITEM( menuItem
), pixmap
);
1168 gtk_signal_connect( GTK_OBJECT(menuItem
), "activate",
1169 GTK_SIGNAL_FUNC(gtk_menu_clicked_callback
),
1172 gtk_menu_append( GTK_MENU(m_menu
), menuItem
);
1173 gtk_widget_show( menuItem
);
1175 appended
= TRUE
; // We've done this, don't do it again
1177 #endif // USE_MENU_BITMAPS
1178 else // a normal item
1180 // text has "_" instead of "&" after mitem->SetText() so don't use it
1181 wxString
text( mitem
->GetText() );
1183 // buffers containing the menu item path and type in multibyte form
1187 strcpy( bufPath
, "/" );
1188 strncat( bufPath
, wxGTK_CONV(text
), WXSIZEOF(bufPath
) - 2 );
1189 bufPath
[WXSIZEOF(bufPath
) - 1] = '\0';
1191 GtkItemFactoryEntry entry
;
1192 entry
.path
= bufPath
;
1193 entry
.callback
= (GtkItemFactoryCallback
) gtk_menu_clicked_callback
;
1194 entry
.callback_action
= 0;
1197 const char *item_type
;
1198 switch ( mitem
->GetKind() )
1201 item_type
= "<CheckItem>";
1205 if ( m_pathLastRadio
.empty() )
1207 // start of a new radio group
1208 item_type
= "<RadioItem>";
1209 wxString
tmp( wxGTK_CONV_BACK( bufPath
) );
1211 m_pathLastRadio
= tmp
;
1213 else // continue the radio group
1215 pathRadio
= m_pathLastRadio
;
1216 pathRadio
.Replace(wxT("_"), wxT(""));
1217 pathRadio
.Prepend(wxT("<main>/"));
1219 strncpy(bufType
, wxGTK_CONV(pathRadio
), WXSIZEOF(bufType
));
1220 bufType
[WXSIZEOF(bufType
) - 1] = '\0';
1221 item_type
= bufType
;
1224 // continue the existing radio group, if any
1225 endOfRadioGroup
= FALSE
;
1229 wxFAIL_MSG( _T("unexpected menu item kind") );
1233 item_type
= "<Item>";
1237 entry
.item_type
= (char *)item_type
; // cast needed for GTK+
1238 entry
.accelerator
= (gchar
*) NULL
;
1241 // due to an apparent bug in GTK+, we have to use a static buffer here -
1242 // otherwise GTK+ 1.2.2 manages to override the memory we pass to it
1244 char s_accel
[50]; // should be big enough, we check for overruns
1245 wxString
tmp( GetHotKey(*mitem
) );
1246 strncpy(s_accel
, wxGTK_CONV( tmp
), WXSIZEOF(s_accel
));
1247 s_accel
[WXSIZEOF(s_accel
) - 1] = '\0';
1248 entry
.accelerator
= s_accel
;
1249 #else // !wxUSE_ACCEL
1250 entry
.accelerator
= (char*) NULL
;
1251 #endif // wxUSE_ACCEL/!wxUSE_ACCEL
1253 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); /* what is 2 ? */
1255 wxString
path( mitem
->GetFactoryPath() );
1256 menuItem
= gtk_item_factory_get_widget( m_factory
, wxGTK_CONV( path
) );
1259 wxLogError( wxT("Wrong menu path: %s\n"), path
.c_str() );
1262 if ( !mitem
->IsSeparator() )
1264 wxASSERT_MSG( menuItem
, wxT("invalid menuitem") );
1266 gtk_signal_connect( GTK_OBJECT(menuItem
), "select",
1267 GTK_SIGNAL_FUNC(gtk_menu_hilight_callback
),
1270 gtk_signal_connect( GTK_OBJECT(menuItem
), "deselect",
1271 GTK_SIGNAL_FUNC(gtk_menu_nolight_callback
),
1275 mitem
->SetMenuItem(menuItem
);
1277 if ( endOfRadioGroup
)
1279 m_pathLastRadio
.clear();
1285 bool wxMenu::DoAppend(wxMenuItem
*mitem
)
1287 return GtkAppend(mitem
) && wxMenuBase::DoAppend(mitem
);
1290 bool wxMenu::DoInsert(size_t pos
, wxMenuItem
*item
)
1292 if ( !wxMenuBase::DoInsert(pos
, item
) )
1295 // GTK+ doesn't have a function to insert a menu using GtkItemFactory (as
1296 // of version 1.2.6), so we first append the item and then change its
1298 if ( !GtkAppend(item
) )
1301 if ( m_style
& wxMENU_TEAROFF
)
1303 // change the position as the first item is the tear-off marker
1307 GtkMenuShell
*menu_shell
= GTK_MENU_SHELL(m_factory
->widget
);
1308 gpointer data
= g_list_last(menu_shell
->children
)->data
;
1309 menu_shell
->children
= g_list_remove(menu_shell
->children
, data
);
1310 menu_shell
->children
= g_list_insert(menu_shell
->children
, data
, pos
);
1315 wxMenuItem
*wxMenu::DoRemove(wxMenuItem
*item
)
1317 if ( !wxMenuBase::DoRemove(item
) )
1318 return (wxMenuItem
*)NULL
;
1320 // TODO: this code doesn't delete the item factory item and this seems
1321 // impossible as of GTK 1.2.6.
1322 gtk_widget_destroy( item
->GetMenuItem() );
1327 int wxMenu::FindMenuIdByMenuItem( GtkWidget
*menuItem
) const
1329 wxMenuItemList::Node
*node
= m_items
.GetFirst();
1332 wxMenuItem
*item
= node
->GetData();
1333 if (item
->GetMenuItem() == menuItem
)
1334 return item
->GetId();
1335 node
= node
->GetNext();
1341 // ----------------------------------------------------------------------------
1343 // ----------------------------------------------------------------------------
1345 #if GTK_CHECK_VERSION(1, 2, 0) && wxUSE_ACCEL
1347 static wxString
GetHotKey( const wxMenuItem
& item
)
1351 wxAcceleratorEntry
*accel
= item
.GetAccel();
1354 int flags
= accel
->GetFlags();
1355 if ( flags
& wxACCEL_ALT
)
1356 hotkey
+= wxT("<alt>");
1357 if ( flags
& wxACCEL_CTRL
)
1358 hotkey
+= wxT("<control>");
1359 if ( flags
& wxACCEL_SHIFT
)
1360 hotkey
+= wxT("<shift>");
1362 int code
= accel
->GetKeyCode();
1377 hotkey
<< wxT('F') << code
- WXK_F1
+ 1;
1380 // TODO: we should use gdk_keyval_name() (a.k.a.
1381 // XKeysymToString) here as well as hardcoding the keysym
1382 // names this might be not portable
1383 case WXK_NUMPAD_INSERT
:
1384 hotkey
<< wxT("KP_Insert" );
1386 case WXK_NUMPAD_DELETE
:
1387 hotkey
<< wxT("KP_Delete" );
1390 hotkey
<< wxT("Insert" );
1393 hotkey
<< wxT("Delete" );
1396 hotkey
<< wxT("Up" );
1399 hotkey
<< wxT("Down" );
1402 hotkey
<< wxT("Prior" );
1405 hotkey
<< wxT("Next" );
1408 hotkey
<< wxT("Left" );
1411 hotkey
<< wxT("Right" );
1414 hotkey
<< wxT("Home" );
1417 hotkey
<< wxT("End" );
1420 hotkey
<< wxT("Return" );
1423 // if there are any other keys wxGetAccelFromString() may
1424 // return, we should process them here
1429 wxString name
= wxGTK_CONV_BACK( gdk_keyval_name((guint
)code
) );
1437 wxFAIL_MSG( wxT("unknown keyboard accel") );
1446 #endif // wxUSE_ACCEL
1449 //-----------------------------------------------------------------------------
1450 // substitute for missing GtkPixmapMenuItem
1451 //-----------------------------------------------------------------------------
1453 #ifdef USE_MENU_BITMAPS
1456 * Copyright (C) 1998, 1999, 2000 Free Software Foundation
1457 * All rights reserved.
1459 * This file is part of the Gnome Library.
1461 * The Gnome Library is free software; you can redistribute it and/or
1462 * modify it under the terms of the GNU Library General Public License as
1463 * published by the Free Software Foundation; either version 2 of the
1464 * License, or (at your option) any later version.
1466 * The Gnome Library is distributed in the hope that it will be useful,
1467 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1468 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1469 * Library General Public License for more details.
1471 * You should have received a copy of the GNU Library General Public
1472 * License along with the Gnome Library; see the file COPYING.LIB. If not,
1473 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
1474 * Boston, MA 02111-1307, USA.
1480 /* Author: Dietmar Maurer <dm@vlsivie.tuwien.ac.at> */
1482 #include <gtk/gtkaccellabel.h>
1483 #include <gtk/gtksignal.h>
1484 #include <gtk/gtkmenuitem.h>
1485 #include <gtk/gtkmenu.h>
1486 #include <gtk/gtkcontainer.h>
1491 static void gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass
*klass
);
1492 static void gtk_pixmap_menu_item_init (GtkPixmapMenuItem
*menu_item
);
1493 static void gtk_pixmap_menu_item_draw (GtkWidget
*widget
,
1494 GdkRectangle
*area
);
1495 static gint
gtk_pixmap_menu_item_expose (GtkWidget
*widget
,
1496 GdkEventExpose
*event
);
1498 /* we must override the following functions */
1500 static void gtk_pixmap_menu_item_map (GtkWidget
*widget
);
1501 static void gtk_pixmap_menu_item_size_allocate (GtkWidget
*widget
,
1502 GtkAllocation
*allocation
);
1503 static void gtk_pixmap_menu_item_forall (GtkContainer
*container
,
1504 gboolean include_internals
,
1505 GtkCallback callback
,
1506 gpointer callback_data
);
1507 static void gtk_pixmap_menu_item_size_request (GtkWidget
*widget
,
1508 GtkRequisition
*requisition
);
1509 static void gtk_pixmap_menu_item_remove (GtkContainer
*container
,
1512 static void changed_have_pixmap_status (GtkPixmapMenuItem
*menu_item
);
1514 static GtkMenuItemClass
*parent_class
= NULL
;
1518 #define BORDER_SPACING 3
1519 #define PMAP_WIDTH 20
1522 gtk_pixmap_menu_item_get_type (void)
1524 static GtkType pixmap_menu_item_type
= 0;
1526 if (!pixmap_menu_item_type
)
1528 GtkTypeInfo pixmap_menu_item_info
=
1530 (char *)"GtkPixmapMenuItem",
1531 sizeof (GtkPixmapMenuItem
),
1532 sizeof (GtkPixmapMenuItemClass
),
1533 (GtkClassInitFunc
) gtk_pixmap_menu_item_class_init
,
1534 (GtkObjectInitFunc
) gtk_pixmap_menu_item_init
,
1535 /* reserved_1 */ NULL
,
1536 /* reserved_2 */ NULL
,
1537 (GtkClassInitFunc
) NULL
,
1540 pixmap_menu_item_type
= gtk_type_unique (gtk_menu_item_get_type (),
1541 &pixmap_menu_item_info
);
1544 return pixmap_menu_item_type
;
1548 * gtk_pixmap_menu_item_new
1550 * Creates a new pixmap menu item. Use gtk_pixmap_menu_item_set_pixmap()
1551 * to set the pixmap wich is displayed at the left side.
1554 * &GtkWidget pointer to new menu item
1558 gtk_pixmap_menu_item_new (void)
1560 return GTK_WIDGET (gtk_type_new (gtk_pixmap_menu_item_get_type ()));
1564 gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass
*klass
)
1566 GtkObjectClass
*object_class
;
1567 GtkWidgetClass
*widget_class
;
1568 GtkMenuItemClass
*menu_item_class
;
1569 GtkContainerClass
*container_class
;
1571 object_class
= (GtkObjectClass
*) klass
;
1572 widget_class
= (GtkWidgetClass
*) klass
;
1573 menu_item_class
= (GtkMenuItemClass
*) klass
;
1574 container_class
= (GtkContainerClass
*) klass
;
1576 parent_class
= (GtkMenuItemClass
*) gtk_type_class (gtk_menu_item_get_type ());
1578 widget_class
->draw
= gtk_pixmap_menu_item_draw
;
1579 widget_class
->expose_event
= gtk_pixmap_menu_item_expose
;
1580 widget_class
->map
= gtk_pixmap_menu_item_map
;
1581 widget_class
->size_allocate
= gtk_pixmap_menu_item_size_allocate
;
1582 widget_class
->size_request
= gtk_pixmap_menu_item_size_request
;
1584 container_class
->forall
= gtk_pixmap_menu_item_forall
;
1585 container_class
->remove
= gtk_pixmap_menu_item_remove
;
1587 klass
->orig_toggle_size
= menu_item_class
->toggle_size
;
1588 klass
->have_pixmap_count
= 0;
1592 gtk_pixmap_menu_item_init (GtkPixmapMenuItem
*menu_item
)
1596 mi
= GTK_MENU_ITEM (menu_item
);
1598 menu_item
->pixmap
= NULL
;
1602 gtk_pixmap_menu_item_draw (GtkWidget
*widget
,
1605 g_return_if_fail (widget
!= NULL
);
1606 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
));
1607 g_return_if_fail (area
!= NULL
);
1609 if (GTK_WIDGET_CLASS (parent_class
)->draw
)
1610 (* GTK_WIDGET_CLASS (parent_class
)->draw
) (widget
, area
);
1612 if (GTK_WIDGET_DRAWABLE (widget
) &&
1613 GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
) {
1614 gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
),NULL
);
1619 gtk_pixmap_menu_item_expose (GtkWidget
*widget
,
1620 GdkEventExpose
*event
)
1622 g_return_val_if_fail (widget
!= NULL
, FALSE
);
1623 g_return_val_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
), FALSE
);
1624 g_return_val_if_fail (event
!= NULL
, FALSE
);
1626 if (GTK_WIDGET_CLASS (parent_class
)->expose_event
)
1627 (* GTK_WIDGET_CLASS (parent_class
)->expose_event
) (widget
, event
);
1629 if (GTK_WIDGET_DRAWABLE (widget
) &&
1630 GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
) {
1631 gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
),NULL
);
1638 * gtk_pixmap_menu_item_set_pixmap
1639 * @menu_item: Pointer to the pixmap menu item
1640 * @pixmap: Pointer to a pixmap widget
1642 * Set the pixmap of the menu item.
1647 gtk_pixmap_menu_item_set_pixmap (GtkPixmapMenuItem
*menu_item
,
1650 g_return_if_fail (menu_item
!= NULL
);
1651 g_return_if_fail (pixmap
!= NULL
);
1652 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (menu_item
));
1653 g_return_if_fail (GTK_IS_WIDGET (pixmap
));
1654 g_return_if_fail (menu_item
->pixmap
== NULL
);
1656 gtk_widget_set_parent (pixmap
, GTK_WIDGET (menu_item
));
1657 menu_item
->pixmap
= pixmap
;
1659 if (GTK_WIDGET_REALIZED (pixmap
->parent
) &&
1660 !GTK_WIDGET_REALIZED (pixmap
))
1661 gtk_widget_realize (pixmap
);
1663 if (GTK_WIDGET_VISIBLE (pixmap
->parent
)) {
1664 if (GTK_WIDGET_MAPPED (pixmap
->parent
) &&
1665 GTK_WIDGET_VISIBLE(pixmap
) &&
1666 !GTK_WIDGET_MAPPED (pixmap
))
1667 gtk_widget_map (pixmap
);
1670 changed_have_pixmap_status(menu_item
);
1672 if (GTK_WIDGET_VISIBLE (pixmap
) && GTK_WIDGET_VISIBLE (menu_item
))
1673 gtk_widget_queue_resize (pixmap
);
1677 gtk_pixmap_menu_item_map (GtkWidget
*widget
)
1679 GtkPixmapMenuItem
*menu_item
;
1681 g_return_if_fail (widget
!= NULL
);
1682 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
));
1684 menu_item
= GTK_PIXMAP_MENU_ITEM(widget
);
1686 GTK_WIDGET_CLASS(parent_class
)->map(widget
);
1688 if (menu_item
->pixmap
&&
1689 GTK_WIDGET_VISIBLE (menu_item
->pixmap
) &&
1690 !GTK_WIDGET_MAPPED (menu_item
->pixmap
))
1691 gtk_widget_map (menu_item
->pixmap
);
1695 gtk_pixmap_menu_item_size_allocate (GtkWidget
*widget
,
1696 GtkAllocation
*allocation
)
1698 GtkPixmapMenuItem
*pmenu_item
;
1700 pmenu_item
= GTK_PIXMAP_MENU_ITEM(widget
);
1702 if (pmenu_item
->pixmap
&& GTK_WIDGET_VISIBLE(pmenu_item
))
1704 GtkAllocation child_allocation
;
1707 border_width
= GTK_CONTAINER (widget
)->border_width
;
1709 child_allocation
.width
= pmenu_item
->pixmap
->requisition
.width
;
1710 child_allocation
.height
= pmenu_item
->pixmap
->requisition
.height
;
1711 child_allocation
.x
= border_width
+ BORDER_SPACING
;
1712 child_allocation
.y
= (border_width
+ BORDER_SPACING
1713 + (((allocation
->height
- child_allocation
.height
) - child_allocation
.x
)
1714 / 2)); /* center pixmaps vertically */
1715 gtk_widget_size_allocate (pmenu_item
->pixmap
, &child_allocation
);
1718 if (GTK_WIDGET_CLASS (parent_class
)->size_allocate
)
1719 GTK_WIDGET_CLASS(parent_class
)->size_allocate (widget
, allocation
);
1723 gtk_pixmap_menu_item_forall (GtkContainer
*container
,
1724 gboolean include_internals
,
1725 GtkCallback callback
,
1726 gpointer callback_data
)
1728 GtkPixmapMenuItem
*menu_item
;
1730 g_return_if_fail (container
!= NULL
);
1731 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container
));
1732 g_return_if_fail (callback
!= NULL
);
1734 menu_item
= GTK_PIXMAP_MENU_ITEM (container
);
1736 if (menu_item
->pixmap
)
1737 (* callback
) (menu_item
->pixmap
, callback_data
);
1739 GTK_CONTAINER_CLASS(parent_class
)->forall(container
,include_internals
,
1740 callback
,callback_data
);
1744 gtk_pixmap_menu_item_size_request (GtkWidget
*widget
,
1745 GtkRequisition
*requisition
)
1747 GtkPixmapMenuItem
*menu_item
;
1748 GtkRequisition req
= {0, 0};
1750 g_return_if_fail (widget
!= NULL
);
1751 g_return_if_fail (GTK_IS_MENU_ITEM (widget
));
1752 g_return_if_fail (requisition
!= NULL
);
1754 GTK_WIDGET_CLASS(parent_class
)->size_request(widget
,requisition
);
1756 menu_item
= GTK_PIXMAP_MENU_ITEM (widget
);
1758 if (menu_item
->pixmap
)
1759 gtk_widget_size_request(menu_item
->pixmap
, &req
);
1761 requisition
->height
= MAX(req
.height
+ GTK_CONTAINER(widget
)->border_width
+ BORDER_SPACING
, (unsigned int) requisition
->height
);
1762 requisition
->width
+= (req
.width
+ GTK_CONTAINER(widget
)->border_width
+ BORDER_SPACING
);
1766 gtk_pixmap_menu_item_remove (GtkContainer
*container
,
1770 gboolean widget_was_visible
;
1772 g_return_if_fail (container
!= NULL
);
1773 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container
));
1774 g_return_if_fail (child
!= NULL
);
1775 g_return_if_fail (GTK_IS_WIDGET (child
));
1777 bin
= GTK_BIN (container
);
1778 g_return_if_fail ((bin
->child
== child
||
1779 (GTK_PIXMAP_MENU_ITEM(container
)->pixmap
== child
)));
1781 widget_was_visible
= GTK_WIDGET_VISIBLE (child
);
1783 gtk_widget_unparent (child
);
1784 if (bin
->child
== child
)
1787 GTK_PIXMAP_MENU_ITEM(container
)->pixmap
= NULL
;
1788 changed_have_pixmap_status(GTK_PIXMAP_MENU_ITEM(container
));
1791 if (widget_was_visible
)
1792 gtk_widget_queue_resize (GTK_WIDGET (container
));
1796 /* important to only call this if there was actually a _change_ in pixmap == NULL */
1798 changed_have_pixmap_status (GtkPixmapMenuItem
*menu_item
)
1800 if (menu_item
->pixmap
!= NULL
) {
1801 GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
+= 1;
1803 if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
== 1) {
1804 /* Install pixmap toggle size */
1805 GTK_MENU_ITEM_GET_CLASS(menu_item
)->toggle_size
= MAX(GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->orig_toggle_size
, PMAP_WIDTH
);
1808 GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
-= 1;
1810 if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
== 0) {
1811 /* Install normal toggle size */
1812 GTK_MENU_ITEM_GET_CLASS(menu_item
)->toggle_size
= GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->orig_toggle_size
;
1816 /* Note that we actually need to do this for _all_ GtkPixmapMenuItem
1817 whenever the klass->toggle_size changes; but by doing it anytime
1818 this function is called, we get the same effect, just because of
1819 how the preferences option to show pixmaps works. Bogus, broken.
1821 if (GTK_WIDGET_VISIBLE(GTK_WIDGET(menu_item
)))
1822 gtk_widget_queue_resize(GTK_WIDGET(menu_item
));
1825 #endif // USE_MENU_BITMAPS