1 /////////////////////////////////////////////////////////////////////////////
4 // Author: Robert Roebling
6 // Copyright: (c) 1998 Robert Roebling
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
11 #pragma implementation "menu.h"
12 #pragma implementation "menuitem.h"
18 #include "wx/bitmap.h"
25 #include "wx/gtk/private.h"
27 #include <gdk/gdkkeysyms.h>
29 // FIXME: is this right? somehow I don't think so (VZ)
31 #include <glib-object.h>
33 #define gtk_accel_group_attach(g, o) _gtk_accel_group_attach((g), (o))
34 #define gtk_accel_group_detach(g, o) _gtk_accel_group_detach((g), (o))
35 #define gtk_menu_ensure_uline_accel_group(m) gtk_menu_get_accel_group(m)
37 #define ACCEL_OBJECT GObject
38 #define ACCEL_OBJECTS(a) (a)->acceleratables
39 #define ACCEL_OBJ_CAST(obj) G_OBJECT(obj)
41 #define ACCEL_OBJECT GtkObject
42 #define ACCEL_OBJECTS(a) (a)->attach_objects
43 #define ACCEL_OBJ_CAST(obj) GTK_OBJECT(obj)
46 //-----------------------------------------------------------------------------
48 //-----------------------------------------------------------------------------
50 extern void wxapp_install_idle_handler();
53 #if GTK_CHECK_VERSION(1, 2, 0) && wxUSE_ACCEL
54 static wxString
GetHotKey( const wxMenuItem
& item
);
57 //-----------------------------------------------------------------------------
58 // substitute for missing GtkPixmapMenuItem
59 //-----------------------------------------------------------------------------
61 // FIXME: I can't make this compile with GTK+ 2.0, disabling for now (VZ)
63 #define USE_MENU_BITMAPS
66 #ifdef USE_MENU_BITMAPS
68 #define GTK_TYPE_PIXMAP_MENU_ITEM (gtk_pixmap_menu_item_get_type ())
69 #define GTK_PIXMAP_MENU_ITEM(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_PIXMAP_MENU_ITEM, GtkPixmapMenuItem))
70 #define GTK_PIXMAP_MENU_ITEM_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_PIXMAP_MENU_ITEM, GtkPixmapMenuItemClass))
71 #define GTK_IS_PIXMAP_MENU_ITEM(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_PIXMAP_MENU_ITEM))
72 #define GTK_IS_PIXMAP_MENU_ITEM_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PIXMAP_MENU_ITEM))
73 //#define GTK_PIXMAP_MENU_ITEM_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_PIXMAP_MENU_ITEM))
74 #define GTK_PIXMAP_MENU_ITEM_GET_CLASS(obj) (GTK_PIXMAP_MENU_ITEM_CLASS( GTK_OBJECT_GET_CLASS(obj)))
76 #ifndef GTK_MENU_ITEM_GET_CLASS
77 #define GTK_MENU_ITEM_GET_CLASS(obj) (GTK_MENU_ITEM_CLASS( GTK_OBJECT_GET_CLASS(obj)))
80 typedef struct _GtkPixmapMenuItem GtkPixmapMenuItem
;
81 typedef struct _GtkPixmapMenuItemClass GtkPixmapMenuItemClass
;
83 struct _GtkPixmapMenuItem
85 GtkMenuItem menu_item
;
90 struct _GtkPixmapMenuItemClass
92 GtkMenuItemClass parent_class
;
94 guint orig_toggle_size
;
95 guint have_pixmap_count
;
99 GtkType
gtk_pixmap_menu_item_get_type (void);
100 GtkWidget
* gtk_pixmap_menu_item_new (void);
101 void gtk_pixmap_menu_item_set_pixmap (GtkPixmapMenuItem
*menu_item
,
104 #endif // USE_MENU_BITMAPS
106 //-----------------------------------------------------------------------------
108 //-----------------------------------------------------------------------------
110 static wxString
wxReplaceUnderscore( const wxString
& title
)
114 /* GTK 1.2 wants to have "_" instead of "&" for accelerators */
117 while (*pc
!= wxT('\0'))
119 if ((*pc
== wxT('&')) && (*(pc
+1) == wxT('&')))
121 // "&" is doubled to indicate "&" instead of accelerator
125 else if (*pc
== wxT('&'))
127 #if GTK_CHECK_VERSION(1, 2, 0)
131 #if GTK_CHECK_VERSION(2, 0, 0)
132 else if (*pc
== wxT('/'))
136 else if (*pc
== wxT('\\'))
140 #elif GTK_CHECK_VERSION(1, 2, 0)
141 else if (*pc
== wxT('/'))
149 if ( *pc
== wxT('_') )
151 // underscores must be doubled to prevent them from being
152 // interpreted as accelerator character prefix by GTK
164 //-----------------------------------------------------------------------------
165 // activate message from GTK
166 //-----------------------------------------------------------------------------
168 static void gtk_menu_open_callback( GtkWidget
*widget
, wxMenu
*menu
)
170 if (g_isIdle
) wxapp_install_idle_handler();
172 wxMenuEvent
event( wxEVT_MENU_OPEN
, -1 );
173 event
.SetEventObject( menu
);
175 wxEvtHandler
* handler
= menu
->GetEventHandler();
176 if (handler
&& handler
->ProcessEvent(event
))
179 wxWindow
*win
= menu
->GetInvokingWindow();
180 if (win
) win
->GetEventHandler()->ProcessEvent( event
);
183 //-----------------------------------------------------------------------------
185 //-----------------------------------------------------------------------------
187 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar
,wxWindow
)
189 wxMenuBar::wxMenuBar( long style
)
191 /* the parent window is known after wxFrame::SetMenu() */
192 m_needParent
= FALSE
;
194 m_invokingWindow
= (wxWindow
*) NULL
;
196 if (!PreCreation( (wxWindow
*) NULL
, wxDefaultPosition
, wxDefaultSize
) ||
197 !CreateBase( (wxWindow
*) NULL
, -1, wxDefaultPosition
, wxDefaultSize
, style
, wxDefaultValidator
, wxT("menubar") ))
199 wxFAIL_MSG( wxT("wxMenuBar creation failed") );
203 m_menus
.DeleteContents( TRUE
);
205 /* GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has. */
206 #if GTK_CHECK_VERSION(1, 2, 1)
207 m_accel
= gtk_accel_group_new();
208 m_factory
= gtk_item_factory_new( GTK_TYPE_MENU_BAR
, "<main>", m_accel
);
209 m_menubar
= gtk_item_factory_get_widget( m_factory
, "<main>" );
211 m_menubar
= gtk_menu_bar_new();
214 if (style
& wxMB_DOCKABLE
)
216 m_widget
= gtk_handle_box_new();
217 gtk_container_add( GTK_CONTAINER(m_widget
), GTK_WIDGET(m_menubar
) );
218 gtk_widget_show( GTK_WIDGET(m_menubar
) );
222 m_widget
= GTK_WIDGET(m_menubar
);
230 wxMenuBar::wxMenuBar()
232 /* the parent window is known after wxFrame::SetMenu() */
233 m_needParent
= FALSE
;
235 m_invokingWindow
= (wxWindow
*) NULL
;
237 if (!PreCreation( (wxWindow
*) NULL
, wxDefaultPosition
, wxDefaultSize
) ||
238 !CreateBase( (wxWindow
*) NULL
, -1, wxDefaultPosition
, wxDefaultSize
, 0, wxDefaultValidator
, wxT("menubar") ))
240 wxFAIL_MSG( wxT("wxMenuBar creation failed") );
244 m_menus
.DeleteContents( TRUE
);
246 /* GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has. */
247 #if GTK_CHECK_VERSION(1, 2, 1)
248 m_accel
= gtk_accel_group_new();
249 m_factory
= gtk_item_factory_new( GTK_TYPE_MENU_BAR
, "<main>", m_accel
);
250 m_menubar
= gtk_item_factory_get_widget( m_factory
, "<main>" );
252 m_menubar
= gtk_menu_bar_new();
255 m_widget
= GTK_WIDGET(m_menubar
);
262 wxMenuBar::~wxMenuBar()
264 // gtk_object_unref( GTK_OBJECT(m_factory) ); why not ?
267 static void wxMenubarUnsetInvokingWindow( wxMenu
*menu
, wxWindow
*win
)
269 menu
->SetInvokingWindow( (wxWindow
*) NULL
);
271 wxWindow
*top_frame
= win
;
272 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
273 top_frame
= top_frame
->GetParent();
275 /* support for native hot keys */
276 gtk_accel_group_detach( menu
->m_accel
, ACCEL_OBJ_CAST(top_frame
->m_widget
) );
278 wxMenuItemList::Node
*node
= menu
->GetMenuItems().GetFirst();
281 wxMenuItem
*menuitem
= node
->GetData();
282 if (menuitem
->IsSubMenu())
283 wxMenubarUnsetInvokingWindow( menuitem
->GetSubMenu(), win
);
284 node
= node
->GetNext();
288 static void wxMenubarSetInvokingWindow( wxMenu
*menu
, wxWindow
*win
)
290 menu
->SetInvokingWindow( win
);
292 #if GTK_CHECK_VERSION(1, 2, 1)
293 wxWindow
*top_frame
= win
;
294 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
295 top_frame
= top_frame
->GetParent();
297 /* support for native hot keys */
298 ACCEL_OBJECT
*obj
= ACCEL_OBJ_CAST(top_frame
->m_widget
);
299 if ( !g_slist_find( ACCEL_OBJECTS(menu
->m_accel
), obj
) )
300 gtk_accel_group_attach( menu
->m_accel
, obj
);
301 #endif // GTK+ 1.2.1+
303 wxMenuItemList::Node
*node
= menu
->GetMenuItems().GetFirst();
306 wxMenuItem
*menuitem
= node
->GetData();
307 if (menuitem
->IsSubMenu())
308 wxMenubarSetInvokingWindow( menuitem
->GetSubMenu(), win
);
309 node
= node
->GetNext();
313 void wxMenuBar::SetInvokingWindow( wxWindow
*win
)
315 m_invokingWindow
= win
;
316 #if GTK_CHECK_VERSION(1, 2, 1)
317 wxWindow
*top_frame
= win
;
318 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
319 top_frame
= top_frame
->GetParent();
321 /* support for native key accelerators indicated by underscroes */
322 ACCEL_OBJECT
*obj
= ACCEL_OBJ_CAST(top_frame
->m_widget
);
323 if ( !g_slist_find( ACCEL_OBJECTS(m_accel
), obj
) )
324 gtk_accel_group_attach( m_accel
, obj
);
325 #endif // GTK+ 1.2.1+
327 wxMenuList::Node
*node
= m_menus
.GetFirst();
330 wxMenu
*menu
= node
->GetData();
331 wxMenubarSetInvokingWindow( menu
, win
);
332 node
= node
->GetNext();
336 void wxMenuBar::UnsetInvokingWindow( wxWindow
*win
)
338 m_invokingWindow
= (wxWindow
*) NULL
;
339 #if GTK_CHECK_VERSION(1, 2, 1)
340 wxWindow
*top_frame
= win
;
341 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
342 top_frame
= top_frame
->GetParent();
344 // support for native key accelerators indicated by underscroes
345 gtk_accel_group_detach( m_accel
, ACCEL_OBJ_CAST(top_frame
->m_widget
) );
346 #endif // GTK+ 1.2.1+
348 wxMenuList::Node
*node
= m_menus
.GetFirst();
351 wxMenu
*menu
= node
->GetData();
352 wxMenubarUnsetInvokingWindow( menu
, win
);
353 node
= node
->GetNext();
357 bool wxMenuBar::Append( wxMenu
*menu
, const wxString
&title
)
359 if ( !wxMenuBarBase::Append( menu
, title
) )
362 return GtkAppend(menu
, title
);
365 bool wxMenuBar::GtkAppend(wxMenu
*menu
, const wxString
& title
)
367 wxString
str( wxReplaceUnderscore( title
) );
369 // This doesn't have much effect right now.
370 menu
->SetTitle( str
);
372 // GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has.
373 #if GTK_CHECK_VERSION(1, 2, 1)
376 buf
<< wxT('/') << str
.c_str();
378 // local buffer in multibyte form
380 strcpy(cbuf
, wxGTK_CONV(buf
) );
382 GtkItemFactoryEntry entry
;
383 entry
.path
= (gchar
*)cbuf
; // const_cast
384 entry
.accelerator
= (gchar
*) NULL
;
385 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
386 entry
.callback_action
= 0;
387 entry
.item_type
= (char *)"<Branch>";
389 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
390 // in order to get the pointer to the item we need the item text _without_ underscores
391 wxString tmp
= wxT("<main>/");
393 for ( pc
= str
; *pc
!= wxT('\0'); pc
++ )
395 // contrary to the common sense, we must throw out _all_ underscores,
396 // (i.e. "Hello__World" => "HelloWorld" and not "Hello_World" as we
397 // might naively think). IMHO it's a bug in GTK+ (VZ)
398 while (*pc
== wxT('_'))
402 menu
->m_owner
= gtk_item_factory_get_item( m_factory
, wxGTK_CONV( tmp
) );
403 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu
->m_owner
), menu
->m_menu
);
406 menu
->m_owner
= gtk_menu_item_new_with_label( wxGTK_CONV( str
) );
407 gtk_widget_show( menu
->m_owner
);
408 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu
->m_owner
), menu
->m_menu
);
410 gtk_menu_bar_append( GTK_MENU_BAR(m_menubar
), menu
->m_owner
);
414 gtk_signal_connect( GTK_OBJECT(menu
->m_owner
), "activate",
415 GTK_SIGNAL_FUNC(gtk_menu_open_callback
),
418 // m_invokingWindow is set after wxFrame::SetMenuBar(). This call enables
419 // addings menu later on.
420 if (m_invokingWindow
)
422 wxMenubarSetInvokingWindow( menu
, m_invokingWindow
);
424 // OPTIMISE ME: we should probably cache this, or pass it
425 // directly, but for now this is a minimal
426 // change to validate the new dynamic sizing.
427 // see (and refactor :) similar code in Remove
430 wxFrame
*frame
= wxDynamicCast( m_invokingWindow
, wxFrame
);
433 frame
->UpdateMenuBarSize();
439 bool wxMenuBar::Insert(size_t pos
, wxMenu
*menu
, const wxString
& title
)
441 if ( !wxMenuBarBase::Insert(pos
, menu
, title
) )
444 // GTK+ doesn't have a function to insert a menu using GtkItemFactory (as
445 // of version 1.2.6), so we first append the item and then change its
447 if ( !GtkAppend(menu
, title
) )
450 if (pos
+1 >= m_menus
.GetCount())
453 GtkMenuShell
*menu_shell
= GTK_MENU_SHELL(m_factory
->widget
);
454 gpointer data
= g_list_last(menu_shell
->children
)->data
;
455 menu_shell
->children
= g_list_remove(menu_shell
->children
, data
);
456 menu_shell
->children
= g_list_insert(menu_shell
->children
, data
, pos
);
461 wxMenu
*wxMenuBar::Replace(size_t pos
, wxMenu
*menu
, const wxString
& title
)
463 // remove the old item and insert a new one
464 wxMenu
*menuOld
= Remove(pos
);
465 if ( menuOld
&& !Insert(pos
, menu
, title
) )
467 return (wxMenu
*) NULL
;
470 // either Insert() succeeded or Remove() failed and menuOld is NULL
474 static wxMenu
*CopyMenu (wxMenu
*menu
)
476 wxMenu
*menucopy
= new wxMenu ();
477 wxMenuItemList::Node
*node
= menu
->GetMenuItems().GetFirst();
480 wxMenuItem
*item
= node
->GetData();
481 int itemid
= item
->GetId();
482 wxString text
= item
->GetText();
483 text
.Replace(wxT("_"), wxT("&"));
484 wxMenu
*submenu
= item
->GetSubMenu();
487 wxMenuItem
* itemcopy
= new wxMenuItem(menucopy
,
489 menu
->GetHelpString(itemid
));
490 itemcopy
->SetBitmap(item
->GetBitmap());
491 itemcopy
->SetCheckable(item
->IsCheckable());
492 menucopy
->Append(itemcopy
);
495 menucopy
->Append (itemid
, text
, CopyMenu(submenu
),
496 menu
->GetHelpString(itemid
));
498 node
= node
->GetNext();
504 wxMenu
*wxMenuBar::Remove(size_t pos
)
506 wxMenu
*menu
= wxMenuBarBase::Remove(pos
);
508 return (wxMenu
*) NULL
;
511 GtkMenuShell *menu_shell = GTK_MENU_SHELL(m_factory->widget);
513 printf( "factory entries before %d\n", (int)g_slist_length(m_factory->items) );
514 printf( "menu shell entries before %d\n", (int)g_list_length( menu_shell->children ) );
517 wxMenu
*menucopy
= CopyMenu( menu
);
519 // unparent calls unref() and that would delete the widget so we raise
520 // the ref count to 2 artificially before invoking unparent.
521 gtk_widget_ref( menu
->m_menu
);
522 gtk_widget_unparent( menu
->m_menu
);
524 gtk_widget_destroy( menu
->m_owner
);
529 printf( "factory entries after %d\n", (int)g_slist_length(m_factory->items) );
530 printf( "menu shell entries after %d\n", (int)g_list_length( menu_shell->children ) );
533 if (m_invokingWindow
)
535 // OPTIMISE ME: see comment in GtkAppend
537 wxFrame
*frame
= wxDynamicCast( m_invokingWindow
, wxFrame
);
540 frame
->UpdateMenuBarSize();
546 static int FindMenuItemRecursive( const wxMenu
*menu
, const wxString
&menuString
, const wxString
&itemString
)
548 if (wxMenuItem::GetLabelFromText(menu
->GetTitle()) == wxMenuItem::GetLabelFromText(menuString
))
550 int res
= menu
->FindItem( itemString
);
551 if (res
!= wxNOT_FOUND
)
555 wxMenuItemList::Node
*node
= menu
->GetMenuItems().GetFirst();
558 wxMenuItem
*item
= node
->GetData();
559 if (item
->IsSubMenu())
560 return FindMenuItemRecursive(item
->GetSubMenu(), menuString
, itemString
);
562 node
= node
->GetNext();
568 int wxMenuBar::FindMenuItem( const wxString
&menuString
, const wxString
&itemString
) const
570 wxMenuList::Node
*node
= m_menus
.GetFirst();
573 wxMenu
*menu
= node
->GetData();
574 int res
= FindMenuItemRecursive( menu
, menuString
, itemString
);
577 node
= node
->GetNext();
583 // Find a wxMenuItem using its id. Recurses down into sub-menus
584 static wxMenuItem
* FindMenuItemByIdRecursive(const wxMenu
* menu
, int id
)
586 wxMenuItem
* result
= menu
->FindChildItem(id
);
588 wxMenuItemList::Node
*node
= menu
->GetMenuItems().GetFirst();
589 while ( node
&& result
== NULL
)
591 wxMenuItem
*item
= node
->GetData();
592 if (item
->IsSubMenu())
594 result
= FindMenuItemByIdRecursive( item
->GetSubMenu(), id
);
596 node
= node
->GetNext();
602 wxMenuItem
* wxMenuBar::FindItem( int id
, wxMenu
**menuForItem
) const
604 wxMenuItem
* result
= 0;
605 wxMenuList::Node
*node
= m_menus
.GetFirst();
606 while (node
&& result
== 0)
608 wxMenu
*menu
= node
->GetData();
609 result
= FindMenuItemByIdRecursive( menu
, id
);
610 node
= node
->GetNext();
615 *menuForItem
= result
? result
->GetMenu() : (wxMenu
*)NULL
;
621 void wxMenuBar::EnableTop( size_t pos
, bool flag
)
623 wxMenuList::Node
*node
= m_menus
.Item( pos
);
625 wxCHECK_RET( node
, wxT("menu not found") );
627 wxMenu
* menu
= node
->GetData();
630 gtk_widget_set_sensitive( menu
->m_owner
, flag
);
633 wxString
wxMenuBar::GetLabelTop( size_t pos
) const
635 wxMenuList::Node
*node
= m_menus
.Item( pos
);
637 wxCHECK_MSG( node
, wxT("invalid"), wxT("menu not found") );
639 wxMenu
* menu
= node
->GetData();
642 wxString
text( menu
->GetTitle() );
643 for ( const wxChar
*pc
= text
.c_str(); *pc
; pc
++ )
645 if ( *pc
== wxT('_') )
647 // '_' is the escape character for GTK+
651 // don't remove ampersands '&' since if we have them in the menu title
652 // it means that they were doubled to indicate "&" instead of accelerator
660 void wxMenuBar::SetLabelTop( size_t pos
, const wxString
& label
)
662 wxMenuList::Node
*node
= m_menus
.Item( pos
);
664 wxCHECK_RET( node
, wxT("menu not found") );
666 wxMenu
* menu
= node
->GetData();
668 wxString
str( wxReplaceUnderscore( label
) );
670 menu
->SetTitle( str
);
674 GtkLabel
*label
= GTK_LABEL( GTK_BIN(menu
->m_owner
)->child
);
677 gtk_label_set( label
, wxGTK_CONV( str
) );
679 /* reparse key accel */
680 (void)gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV( str
) );
681 gtk_accel_label_refetch( GTK_ACCEL_LABEL(label
) );
686 //-----------------------------------------------------------------------------
688 //-----------------------------------------------------------------------------
690 static void gtk_menu_clicked_callback( GtkWidget
*widget
, wxMenu
*menu
)
693 wxapp_install_idle_handler();
695 int id
= menu
->FindMenuIdByMenuItem(widget
);
697 /* should find it for normal (not popup) menu */
698 wxASSERT_MSG( (id
!= -1) || (menu
->GetInvokingWindow() != NULL
),
699 _T("menu item not found in gtk_menu_clicked_callback") );
701 if (!menu
->IsEnabled(id
))
704 wxMenuItem
* item
= menu
->FindChildItem( id
);
705 wxCHECK_RET( item
, wxT("error in menu item callback") );
707 if (item
->IsCheckable())
709 bool isReallyChecked
= item
->IsChecked(),
710 isInternallyChecked
= item
->wxMenuItemBase::IsChecked();
712 // ensure that the internal state is always consistent with what is
713 // shown on the screen
714 item
->wxMenuItemBase::Check(isReallyChecked
);
716 // we must not report the events for the radio button going up nor the
717 // events resulting from the calls to wxMenuItem::Check()
718 if ( (item
->GetKind() == wxITEM_RADIO
&& !isReallyChecked
) ||
719 (isInternallyChecked
== isReallyChecked
) )
726 // Is this menu on a menubar? (possibly nested)
727 wxFrame
* frame
= NULL
;
729 while ( pm
&& !frame
)
731 if ( pm
->IsAttached() )
732 frame
= pm
->GetMenuBar()->GetFrame();
733 pm
= pm
->GetParent();
738 // If it is attached then let the frame send the event.
739 // Don't call frame->ProcessCommand(id) because it toggles
740 // checkable items and we've already done that above.
741 wxCommandEvent
commandEvent(wxEVT_COMMAND_MENU_SELECTED
, id
);
742 commandEvent
.SetEventObject(frame
);
743 if (item
->IsCheckable())
744 commandEvent
.SetInt(item
->IsChecked());
746 frame
->GetEventHandler()->ProcessEvent(commandEvent
);
750 // otherwise let the menu have it
751 menu
->SendEvent(id
, item
->IsCheckable() ? item
->IsChecked() : -1);
755 //-----------------------------------------------------------------------------
757 //-----------------------------------------------------------------------------
759 static void gtk_menu_hilight_callback( GtkWidget
*widget
, wxMenu
*menu
)
761 if (g_isIdle
) wxapp_install_idle_handler();
763 int id
= menu
->FindMenuIdByMenuItem(widget
);
765 wxASSERT( id
!= -1 ); // should find it!
767 if (!menu
->IsEnabled(id
))
770 wxMenuEvent
event( wxEVT_MENU_HIGHLIGHT
, id
);
771 event
.SetEventObject( menu
);
773 wxEvtHandler
* handler
= menu
->GetEventHandler();
774 if (handler
&& handler
->ProcessEvent(event
))
777 wxWindow
*win
= menu
->GetInvokingWindow();
778 if (win
) win
->GetEventHandler()->ProcessEvent( event
);
781 //-----------------------------------------------------------------------------
783 //-----------------------------------------------------------------------------
785 static void gtk_menu_nolight_callback( GtkWidget
*widget
, wxMenu
*menu
)
787 if (g_isIdle
) wxapp_install_idle_handler();
789 int id
= menu
->FindMenuIdByMenuItem(widget
);
791 wxASSERT( id
!= -1 ); // should find it!
793 if (!menu
->IsEnabled(id
))
796 wxMenuEvent
event( wxEVT_MENU_HIGHLIGHT
, -1 );
797 event
.SetEventObject( menu
);
799 wxEvtHandler
* handler
= menu
->GetEventHandler();
800 if (handler
&& handler
->ProcessEvent(event
))
803 wxWindow
*win
= menu
->GetInvokingWindow();
805 win
->GetEventHandler()->ProcessEvent( event
);
808 //-----------------------------------------------------------------------------
810 //-----------------------------------------------------------------------------
812 IMPLEMENT_DYNAMIC_CLASS(wxMenuItem
, wxObject
)
814 wxMenuItem
*wxMenuItemBase::New(wxMenu
*parentMenu
,
816 const wxString
& name
,
817 const wxString
& help
,
821 return new wxMenuItem(parentMenu
, id
, name
, help
, kind
, subMenu
);
824 wxMenuItem::wxMenuItem(wxMenu
*parentMenu
,
826 const wxString
& text
,
827 const wxString
& help
,
830 : wxMenuItemBase(parentMenu
, id
, text
, help
, kind
, subMenu
)
835 wxMenuItem::wxMenuItem(wxMenu
*parentMenu
,
837 const wxString
& text
,
838 const wxString
& help
,
841 : wxMenuItemBase(parentMenu
, id
, text
, help
,
842 isCheckable
? wxITEM_CHECK
: wxITEM_NORMAL
, subMenu
)
847 void wxMenuItem::Init(const wxString
& text
)
849 m_labelWidget
= (GtkWidget
*) NULL
;
850 m_menuItem
= (GtkWidget
*) NULL
;
855 wxMenuItem::~wxMenuItem()
857 // don't delete menu items, the menus take care of that
860 // return the menu item text without any menu accels
862 wxString
wxMenuItemBase::GetLabelFromText(const wxString
& text
)
866 for ( const wxChar
*pc
= text
.c_str(); *pc
; pc
++ )
868 if ( *pc
== wxT('_') )
870 // GTK 1.2 escapes "xxx_xxx" to "xxx__xxx"
876 #if GTK_CHECK_VERSION(2, 0, 0)
877 if ( *pc
== wxT('\\') )
879 // GTK 2.0 escapes "xxx/xxx" to "xxx\/xxx"
886 if ( (*pc
== wxT('&')) && (*(pc
+1) != wxT('&')) )
889 // "&" is doubled to indicate "&" instead of accelerator
896 // wxPrintf( L"text %s label %s\n", text.c_str(), label.c_str() );
901 void wxMenuItem::SetText( const wxString
& str
)
903 // Some optimization to avoid flicker
904 wxString oldLabel
= m_text
;
905 oldLabel
= wxStripMenuCodes(oldLabel
.BeforeFirst('\t'));
906 oldLabel
.Replace(wxT("_"), wxT(""));
907 wxString label1
= wxStripMenuCodes(str
.BeforeFirst('\t'));
908 if (oldLabel
== label1
)
917 label
= (GtkLabel
*) m_labelWidget
;
919 label
= GTK_LABEL( GTK_BIN(m_menuItem
)->child
);
921 #if GTK_CHECK_VERSION(2, 0, 0)
922 // We have to imitate item_factory_unescape_label here
924 for (size_t n
= 0; n
< m_text
.Len(); n
++)
926 if (m_text
[n
] != wxT('\\'))
930 gtk_label_set_text_with_mnemonic( GTK_LABEL(label
), wxGTK_CONV(tmp
) );
933 gtk_label_set( label
, wxGTK_CONV( m_text
) );
936 (void)gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV(m_text
) );
937 gtk_accel_label_refetch( GTK_ACCEL_LABEL(label
) );
942 // it's valid for this function to be called even if m_menuItem == NULL
943 void wxMenuItem::DoSetText( const wxString
& str
)
945 // '\t' is the deliminator indicating a hot key
947 const wxChar
*pc
= str
;
948 while ( (*pc
!= wxT('\0')) && (*pc
!= wxT('\t')) )
950 if ((*pc
== wxT('&')) && (*(pc
+1) == wxT('&')))
952 // "&" is doubled to indicate "&" instead of accelerator
956 else if (*pc
== wxT('&'))
960 #if GTK_CHECK_VERSION(2, 0, 0)
961 else if ( *pc
== wxT('_') ) // escape underscores
963 // m_text << wxT("__"); doesn't work
966 else if (*pc
== wxT('/')) // we have to escape slashes
968 m_text
<< wxT("\\/");
970 else if (*pc
== wxT('\\')) // we have to double backslashes
972 m_text
<< wxT("\\\\");
975 else if ( *pc
== wxT('_') ) // escape underscores
979 else if (*pc
== wxT('/')) /* we have to filter out slashes ... */
981 m_text
<< wxT('\\'); /* ... and replace them with back slashes */
990 // wxPrintf( L"str %s m_text %s\n", str.c_str(), m_text.c_str() );
1003 wxAcceleratorEntry
*wxMenuItem::GetAccel() const
1008 return (wxAcceleratorEntry
*)NULL
;
1011 // as wxGetAccelFromString() looks for TAB, insert a dummy one here
1013 label
<< wxT('\t') << GetHotKey();
1015 return wxGetAccelFromString(label
);
1018 #endif // wxUSE_ACCEL
1020 void wxMenuItem::Check( bool check
)
1022 wxCHECK_RET( m_menuItem
, wxT("invalid menu item") );
1024 if (check
== m_isChecked
)
1027 wxMenuItemBase::Check( check
);
1029 switch ( GetKind() )
1033 gtk_check_menu_item_set_state( (GtkCheckMenuItem
*)m_menuItem
, (gint
)check
);
1037 wxFAIL_MSG( _T("can't check this item") );
1041 void wxMenuItem::Enable( bool enable
)
1043 wxCHECK_RET( m_menuItem
, wxT("invalid menu item") );
1045 gtk_widget_set_sensitive( m_menuItem
, enable
);
1046 wxMenuItemBase::Enable( enable
);
1049 bool wxMenuItem::IsChecked() const
1051 wxCHECK_MSG( m_menuItem
, FALSE
, wxT("invalid menu item") );
1053 wxCHECK_MSG( IsCheckable(), FALSE
,
1054 wxT("can't get state of uncheckable item!") );
1056 return ((GtkCheckMenuItem
*)m_menuItem
)->active
!= 0;
1059 wxString
wxMenuItem::GetFactoryPath() const
1061 // In order to get the pointer to the item we need the item
1062 // text _without_ underscores in GTK 1.2
1063 wxString
path( wxT("<main>/") );
1065 for ( const wxChar
*pc
= m_text
.c_str(); *pc
; pc
++ )
1067 if ( *pc
== wxT('_') )
1072 // remove '_' unconditionally
1077 // don't remove ampersands '&' since if we have them in the menu item title
1078 // it means that they were doubled to indicate "&" instead of accelerator
1086 //-----------------------------------------------------------------------------
1088 //-----------------------------------------------------------------------------
1090 IMPLEMENT_DYNAMIC_CLASS(wxMenu
,wxEvtHandler
)
1094 m_accel
= gtk_accel_group_new();
1095 m_factory
= gtk_item_factory_new( GTK_TYPE_MENU
, "<main>", m_accel
);
1096 m_menu
= gtk_item_factory_get_widget( m_factory
, "<main>" );
1098 m_owner
= (GtkWidget
*) NULL
;
1100 // Tearoffs are entries, just like separators. So if we want this
1101 // menu to be a tear-off one, we just append a tearoff entry
1103 if(m_style
& wxMENU_TEAROFF
)
1105 GtkItemFactoryEntry entry
;
1106 entry
.path
= (char *)"/tearoff";
1107 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
1108 entry
.callback_action
= 0;
1109 entry
.item_type
= (char *)"<Tearoff>";
1110 entry
.accelerator
= (gchar
*) NULL
;
1111 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
1112 //GtkWidget *menuItem = gtk_item_factory_get_widget( m_factory, "<main>/tearoff" );
1115 // append the title as the very first entry if we have it
1118 Append(-2, m_title
);
1127 gtk_widget_destroy( m_menu
);
1129 gtk_object_unref( GTK_OBJECT(m_factory
) );
1132 bool wxMenu::GtkAppend(wxMenuItem
*mitem
)
1134 GtkWidget
*menuItem
;
1136 #if defined(USE_MENU_BITMAPS) || !GTK_CHECK_VERSION(1, 2, 0)
1137 bool appended
= FALSE
;
1140 // does this item terminate the current radio group?
1141 bool endOfRadioGroup
= TRUE
;
1143 if ( mitem
->IsSeparator() )
1145 GtkItemFactoryEntry entry
;
1146 entry
.path
= (char *)"/sep";
1147 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
1148 entry
.callback_action
= 0;
1149 entry
.item_type
= (char *)"<Separator>";
1150 entry
.accelerator
= (gchar
*) NULL
;
1152 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
1154 // this will be wrong for more than one separator. do we care?
1155 menuItem
= gtk_item_factory_get_widget( m_factory
, "<main>/sep" );
1157 // we might have a separator inside a radio group
1158 endOfRadioGroup
= FALSE
;
1160 else if ( mitem
->IsSubMenu() )
1162 // text has "_" instead of "&" after mitem->SetText()
1163 wxString
text( mitem
->GetText() );
1165 // local buffer in multibyte form
1168 strcat( buf
, wxGTK_CONV( text
) );
1170 GtkItemFactoryEntry entry
;
1172 entry
.callback
= (GtkItemFactoryCallback
) 0;
1173 entry
.callback_action
= 0;
1174 entry
.item_type
= (char *)"<Branch>";
1175 entry
.accelerator
= (gchar
*) NULL
;
1177 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
1179 wxString
path( mitem
->GetFactoryPath() );
1180 menuItem
= gtk_item_factory_get_item( m_factory
, wxGTK_CONV( path
) );
1182 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menuItem
), mitem
->GetSubMenu()->m_menu
);
1184 // if adding a submenu to a menu already existing in the menu bar, we
1185 // must set invoking window to allow processing events from this
1187 if ( m_invokingWindow
)
1188 wxMenubarSetInvokingWindow(mitem
->GetSubMenu(), m_invokingWindow
);
1190 #ifdef USE_MENU_BITMAPS
1191 else if (mitem
->GetBitmap().Ok()) // An item with bitmap
1193 wxString
text( mitem
->GetText() );
1194 const wxBitmap
*bitmap
= &mitem
->GetBitmap();
1196 menuItem
= gtk_pixmap_menu_item_new ();
1197 GtkWidget
*label
= gtk_accel_label_new ( wxGTK_CONV( text
) );
1198 gtk_misc_set_alignment (GTK_MISC (label
), 0.0, 0.5);
1199 gtk_container_add (GTK_CONTAINER (menuItem
), label
);
1200 guint accel_key
= gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV( text
) );
1201 gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (label
), menuItem
);
1202 if (accel_key
!= GDK_VoidSymbol
)
1204 gtk_widget_add_accelerator (menuItem
,
1206 gtk_menu_ensure_uline_accel_group (GTK_MENU (m_menu
)),
1210 gtk_widget_show (label
);
1212 mitem
->SetLabelWidget(label
);
1214 GtkWidget
* pixmap
= gtk_pixmap_new( bitmap
->GetPixmap(), bitmap
->GetMask() ? bitmap
->GetMask()->GetBitmap() : (GdkBitmap
* )NULL
);
1215 gtk_widget_show(pixmap
);
1216 gtk_pixmap_menu_item_set_pixmap(GTK_PIXMAP_MENU_ITEM( menuItem
), pixmap
);
1218 gtk_signal_connect( GTK_OBJECT(menuItem
), "activate",
1219 GTK_SIGNAL_FUNC(gtk_menu_clicked_callback
),
1222 gtk_menu_append( GTK_MENU(m_menu
), menuItem
);
1223 gtk_widget_show( menuItem
);
1225 appended
= TRUE
; // We've done this, don't do it again
1227 #endif // USE_MENU_BITMAPS
1228 else // a normal item
1230 // text has "_" instead of "&" after mitem->SetText() so don't use it
1231 wxString
text( mitem
->GetText() );
1233 // buffers containing the menu item path and type in multibyte form
1237 strcpy( bufPath
, "/" );
1238 strncat( bufPath
, wxGTK_CONV(text
), WXSIZEOF(bufPath
) - 2 );
1239 bufPath
[WXSIZEOF(bufPath
) - 1] = '\0';
1241 GtkItemFactoryEntry entry
;
1242 entry
.path
= bufPath
;
1243 entry
.callback
= (GtkItemFactoryCallback
) gtk_menu_clicked_callback
;
1244 entry
.callback_action
= 0;
1247 const char *item_type
;
1248 switch ( mitem
->GetKind() )
1251 item_type
= "<CheckItem>";
1255 if ( m_pathLastRadio
.empty() )
1257 // start of a new radio group
1258 item_type
= "<RadioItem>";
1259 wxString
tmp( wxGTK_CONV_BACK( bufPath
) );
1261 m_pathLastRadio
= tmp
;
1263 else // continue the radio group
1265 pathRadio
= m_pathLastRadio
;
1266 pathRadio
.Replace(wxT("_"), wxT(""));
1267 pathRadio
.Prepend(wxT("<main>/"));
1269 strncpy(bufType
, wxGTK_CONV(pathRadio
), WXSIZEOF(bufType
));
1270 bufType
[WXSIZEOF(bufType
) - 1] = '\0';
1271 item_type
= bufType
;
1274 // continue the existing radio group, if any
1275 endOfRadioGroup
= FALSE
;
1279 wxFAIL_MSG( _T("unexpected menu item kind") );
1283 item_type
= "<Item>";
1287 entry
.item_type
= (char *)item_type
; // cast needed for GTK+
1288 entry
.accelerator
= (gchar
*) NULL
;
1291 // due to an apparent bug in GTK+, we have to use a static buffer here -
1292 // otherwise GTK+ 1.2.2 manages to override the memory we pass to it
1294 char s_accel
[50]; // should be big enough, we check for overruns
1295 wxString
tmp( GetHotKey(*mitem
) );
1296 strncpy(s_accel
, wxGTK_CONV( tmp
), WXSIZEOF(s_accel
));
1297 s_accel
[WXSIZEOF(s_accel
) - 1] = '\0';
1298 entry
.accelerator
= s_accel
;
1299 #else // !wxUSE_ACCEL
1300 entry
.accelerator
= (char*) NULL
;
1301 #endif // wxUSE_ACCEL/!wxUSE_ACCEL
1303 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); /* what is 2 ? */
1305 wxString
path( mitem
->GetFactoryPath() );
1306 menuItem
= gtk_item_factory_get_widget( m_factory
, wxGTK_CONV( path
) );
1309 wxLogError( wxT("Wrong menu path: %s\n"), path
.c_str() );
1312 if ( !mitem
->IsSeparator() )
1314 wxASSERT_MSG( menuItem
, wxT("invalid menuitem") );
1316 gtk_signal_connect( GTK_OBJECT(menuItem
), "select",
1317 GTK_SIGNAL_FUNC(gtk_menu_hilight_callback
),
1320 gtk_signal_connect( GTK_OBJECT(menuItem
), "deselect",
1321 GTK_SIGNAL_FUNC(gtk_menu_nolight_callback
),
1325 mitem
->SetMenuItem(menuItem
);
1327 if ( endOfRadioGroup
)
1329 m_pathLastRadio
.clear();
1335 bool wxMenu::DoAppend(wxMenuItem
*mitem
)
1337 return GtkAppend(mitem
) && wxMenuBase::DoAppend(mitem
);
1340 bool wxMenu::DoInsert(size_t pos
, wxMenuItem
*item
)
1342 if ( !wxMenuBase::DoInsert(pos
, item
) )
1345 // GTK+ doesn't have a function to insert a menu using GtkItemFactory (as
1346 // of version 1.2.6), so we first append the item and then change its
1348 if ( !GtkAppend(item
) )
1351 if ( m_style
& wxMENU_TEAROFF
)
1353 // change the position as the first item is the tear-off marker
1357 GtkMenuShell
*menu_shell
= GTK_MENU_SHELL(m_factory
->widget
);
1358 gpointer data
= g_list_last(menu_shell
->children
)->data
;
1359 menu_shell
->children
= g_list_remove(menu_shell
->children
, data
);
1360 menu_shell
->children
= g_list_insert(menu_shell
->children
, data
, pos
);
1365 wxMenuItem
*wxMenu::DoRemove(wxMenuItem
*item
)
1367 if ( !wxMenuBase::DoRemove(item
) )
1368 return (wxMenuItem
*)NULL
;
1370 // TODO: this code doesn't delete the item factory item and this seems
1371 // impossible as of GTK 1.2.6.
1372 gtk_widget_destroy( item
->GetMenuItem() );
1377 int wxMenu::FindMenuIdByMenuItem( GtkWidget
*menuItem
) const
1379 wxMenuItemList::Node
*node
= m_items
.GetFirst();
1382 wxMenuItem
*item
= node
->GetData();
1383 if (item
->GetMenuItem() == menuItem
)
1384 return item
->GetId();
1385 node
= node
->GetNext();
1391 // ----------------------------------------------------------------------------
1393 // ----------------------------------------------------------------------------
1395 #if GTK_CHECK_VERSION(1, 2, 0) && wxUSE_ACCEL
1397 static wxString
GetHotKey( const wxMenuItem
& item
)
1401 wxAcceleratorEntry
*accel
= item
.GetAccel();
1404 int flags
= accel
->GetFlags();
1405 if ( flags
& wxACCEL_ALT
)
1406 hotkey
+= wxT("<alt>");
1407 if ( flags
& wxACCEL_CTRL
)
1408 hotkey
+= wxT("<control>");
1409 if ( flags
& wxACCEL_SHIFT
)
1410 hotkey
+= wxT("<shift>");
1412 int code
= accel
->GetKeyCode();
1427 hotkey
<< wxT('F') << code
- WXK_F1
+ 1;
1430 // TODO: we should use gdk_keyval_name() (a.k.a.
1431 // XKeysymToString) here as well as hardcoding the keysym
1432 // names this might be not portable
1433 case WXK_NUMPAD_INSERT
:
1434 hotkey
<< wxT("KP_Insert" );
1436 case WXK_NUMPAD_DELETE
:
1437 hotkey
<< wxT("KP_Delete" );
1440 hotkey
<< wxT("Insert" );
1443 hotkey
<< wxT("Delete" );
1446 hotkey
<< wxT("Up" );
1449 hotkey
<< wxT("Down" );
1452 hotkey
<< wxT("Prior" );
1455 hotkey
<< wxT("Next" );
1458 hotkey
<< wxT("Left" );
1461 hotkey
<< wxT("Right" );
1464 hotkey
<< wxT("Home" );
1467 hotkey
<< wxT("End" );
1470 hotkey
<< wxT("Return" );
1473 // if there are any other keys wxGetAccelFromString() may
1474 // return, we should process them here
1479 wxString name
= wxGTK_CONV_BACK( gdk_keyval_name((guint
)code
) );
1487 wxFAIL_MSG( wxT("unknown keyboard accel") );
1496 #endif // wxUSE_ACCEL
1499 //-----------------------------------------------------------------------------
1500 // substitute for missing GtkPixmapMenuItem
1501 //-----------------------------------------------------------------------------
1503 #ifdef USE_MENU_BITMAPS
1506 * Copyright (C) 1998, 1999, 2000 Free Software Foundation
1507 * All rights reserved.
1509 * This file is part of the Gnome Library.
1511 * The Gnome Library is free software; you can redistribute it and/or
1512 * modify it under the terms of the GNU Library General Public License as
1513 * published by the Free Software Foundation; either version 2 of the
1514 * License, or (at your option) any later version.
1516 * The Gnome Library is distributed in the hope that it will be useful,
1517 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1518 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1519 * Library General Public License for more details.
1521 * You should have received a copy of the GNU Library General Public
1522 * License along with the Gnome Library; see the file COPYING.LIB. If not,
1523 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
1524 * Boston, MA 02111-1307, USA.
1530 /* Author: Dietmar Maurer <dm@vlsivie.tuwien.ac.at> */
1532 #include <gtk/gtkaccellabel.h>
1533 #include <gtk/gtksignal.h>
1534 #include <gtk/gtkmenuitem.h>
1535 #include <gtk/gtkmenu.h>
1536 #include <gtk/gtkcontainer.h>
1541 static void gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass
*klass
);
1542 static void gtk_pixmap_menu_item_init (GtkPixmapMenuItem
*menu_item
);
1543 static void gtk_pixmap_menu_item_draw (GtkWidget
*widget
,
1544 GdkRectangle
*area
);
1545 static gint
gtk_pixmap_menu_item_expose (GtkWidget
*widget
,
1546 GdkEventExpose
*event
);
1548 /* we must override the following functions */
1550 static void gtk_pixmap_menu_item_map (GtkWidget
*widget
);
1551 static void gtk_pixmap_menu_item_size_allocate (GtkWidget
*widget
,
1552 GtkAllocation
*allocation
);
1553 static void gtk_pixmap_menu_item_forall (GtkContainer
*container
,
1554 gboolean include_internals
,
1555 GtkCallback callback
,
1556 gpointer callback_data
);
1557 static void gtk_pixmap_menu_item_size_request (GtkWidget
*widget
,
1558 GtkRequisition
*requisition
);
1559 static void gtk_pixmap_menu_item_remove (GtkContainer
*container
,
1562 static void changed_have_pixmap_status (GtkPixmapMenuItem
*menu_item
);
1564 static GtkMenuItemClass
*parent_class
= NULL
;
1568 #define BORDER_SPACING 3
1569 #define PMAP_WIDTH 20
1572 gtk_pixmap_menu_item_get_type (void)
1574 static GtkType pixmap_menu_item_type
= 0;
1576 if (!pixmap_menu_item_type
)
1578 GtkTypeInfo pixmap_menu_item_info
=
1580 (char *)"GtkPixmapMenuItem",
1581 sizeof (GtkPixmapMenuItem
),
1582 sizeof (GtkPixmapMenuItemClass
),
1583 (GtkClassInitFunc
) gtk_pixmap_menu_item_class_init
,
1584 (GtkObjectInitFunc
) gtk_pixmap_menu_item_init
,
1585 /* reserved_1 */ NULL
,
1586 /* reserved_2 */ NULL
,
1587 (GtkClassInitFunc
) NULL
,
1590 pixmap_menu_item_type
= gtk_type_unique (gtk_menu_item_get_type (),
1591 &pixmap_menu_item_info
);
1594 return pixmap_menu_item_type
;
1598 * gtk_pixmap_menu_item_new
1600 * Creates a new pixmap menu item. Use gtk_pixmap_menu_item_set_pixmap()
1601 * to set the pixmap wich is displayed at the left side.
1604 * &GtkWidget pointer to new menu item
1608 gtk_pixmap_menu_item_new (void)
1610 return GTK_WIDGET (gtk_type_new (gtk_pixmap_menu_item_get_type ()));
1614 gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass
*klass
)
1616 GtkObjectClass
*object_class
;
1617 GtkWidgetClass
*widget_class
;
1618 GtkMenuItemClass
*menu_item_class
;
1619 GtkContainerClass
*container_class
;
1621 object_class
= (GtkObjectClass
*) klass
;
1622 widget_class
= (GtkWidgetClass
*) klass
;
1623 menu_item_class
= (GtkMenuItemClass
*) klass
;
1624 container_class
= (GtkContainerClass
*) klass
;
1626 parent_class
= (GtkMenuItemClass
*) gtk_type_class (gtk_menu_item_get_type ());
1628 widget_class
->draw
= gtk_pixmap_menu_item_draw
;
1629 widget_class
->expose_event
= gtk_pixmap_menu_item_expose
;
1630 widget_class
->map
= gtk_pixmap_menu_item_map
;
1631 widget_class
->size_allocate
= gtk_pixmap_menu_item_size_allocate
;
1632 widget_class
->size_request
= gtk_pixmap_menu_item_size_request
;
1634 container_class
->forall
= gtk_pixmap_menu_item_forall
;
1635 container_class
->remove
= gtk_pixmap_menu_item_remove
;
1637 klass
->orig_toggle_size
= menu_item_class
->toggle_size
;
1638 klass
->have_pixmap_count
= 0;
1642 gtk_pixmap_menu_item_init (GtkPixmapMenuItem
*menu_item
)
1646 mi
= GTK_MENU_ITEM (menu_item
);
1648 menu_item
->pixmap
= NULL
;
1652 gtk_pixmap_menu_item_draw (GtkWidget
*widget
,
1655 g_return_if_fail (widget
!= NULL
);
1656 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
));
1657 g_return_if_fail (area
!= NULL
);
1659 if (GTK_WIDGET_CLASS (parent_class
)->draw
)
1660 (* GTK_WIDGET_CLASS (parent_class
)->draw
) (widget
, area
);
1662 if (GTK_WIDGET_DRAWABLE (widget
) &&
1663 GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
) {
1664 gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
),NULL
);
1669 gtk_pixmap_menu_item_expose (GtkWidget
*widget
,
1670 GdkEventExpose
*event
)
1672 g_return_val_if_fail (widget
!= NULL
, FALSE
);
1673 g_return_val_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
), FALSE
);
1674 g_return_val_if_fail (event
!= NULL
, FALSE
);
1676 if (GTK_WIDGET_CLASS (parent_class
)->expose_event
)
1677 (* GTK_WIDGET_CLASS (parent_class
)->expose_event
) (widget
, event
);
1679 if (GTK_WIDGET_DRAWABLE (widget
) &&
1680 GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
) {
1681 gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
),NULL
);
1688 * gtk_pixmap_menu_item_set_pixmap
1689 * @menu_item: Pointer to the pixmap menu item
1690 * @pixmap: Pointer to a pixmap widget
1692 * Set the pixmap of the menu item.
1697 gtk_pixmap_menu_item_set_pixmap (GtkPixmapMenuItem
*menu_item
,
1700 g_return_if_fail (menu_item
!= NULL
);
1701 g_return_if_fail (pixmap
!= NULL
);
1702 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (menu_item
));
1703 g_return_if_fail (GTK_IS_WIDGET (pixmap
));
1704 g_return_if_fail (menu_item
->pixmap
== NULL
);
1706 gtk_widget_set_parent (pixmap
, GTK_WIDGET (menu_item
));
1707 menu_item
->pixmap
= pixmap
;
1709 if (GTK_WIDGET_REALIZED (pixmap
->parent
) &&
1710 !GTK_WIDGET_REALIZED (pixmap
))
1711 gtk_widget_realize (pixmap
);
1713 if (GTK_WIDGET_VISIBLE (pixmap
->parent
)) {
1714 if (GTK_WIDGET_MAPPED (pixmap
->parent
) &&
1715 GTK_WIDGET_VISIBLE(pixmap
) &&
1716 !GTK_WIDGET_MAPPED (pixmap
))
1717 gtk_widget_map (pixmap
);
1720 changed_have_pixmap_status(menu_item
);
1722 if (GTK_WIDGET_VISIBLE (pixmap
) && GTK_WIDGET_VISIBLE (menu_item
))
1723 gtk_widget_queue_resize (pixmap
);
1727 gtk_pixmap_menu_item_map (GtkWidget
*widget
)
1729 GtkPixmapMenuItem
*menu_item
;
1731 g_return_if_fail (widget
!= NULL
);
1732 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
));
1734 menu_item
= GTK_PIXMAP_MENU_ITEM(widget
);
1736 GTK_WIDGET_CLASS(parent_class
)->map(widget
);
1738 if (menu_item
->pixmap
&&
1739 GTK_WIDGET_VISIBLE (menu_item
->pixmap
) &&
1740 !GTK_WIDGET_MAPPED (menu_item
->pixmap
))
1741 gtk_widget_map (menu_item
->pixmap
);
1745 gtk_pixmap_menu_item_size_allocate (GtkWidget
*widget
,
1746 GtkAllocation
*allocation
)
1748 GtkPixmapMenuItem
*pmenu_item
;
1750 pmenu_item
= GTK_PIXMAP_MENU_ITEM(widget
);
1752 if (pmenu_item
->pixmap
&& GTK_WIDGET_VISIBLE(pmenu_item
))
1754 GtkAllocation child_allocation
;
1757 border_width
= GTK_CONTAINER (widget
)->border_width
;
1759 child_allocation
.width
= pmenu_item
->pixmap
->requisition
.width
;
1760 child_allocation
.height
= pmenu_item
->pixmap
->requisition
.height
;
1761 child_allocation
.x
= border_width
+ BORDER_SPACING
;
1762 child_allocation
.y
= (border_width
+ BORDER_SPACING
1763 + (((allocation
->height
- child_allocation
.height
) - child_allocation
.x
)
1764 / 2)); /* center pixmaps vertically */
1765 gtk_widget_size_allocate (pmenu_item
->pixmap
, &child_allocation
);
1768 if (GTK_WIDGET_CLASS (parent_class
)->size_allocate
)
1769 GTK_WIDGET_CLASS(parent_class
)->size_allocate (widget
, allocation
);
1773 gtk_pixmap_menu_item_forall (GtkContainer
*container
,
1774 gboolean include_internals
,
1775 GtkCallback callback
,
1776 gpointer callback_data
)
1778 GtkPixmapMenuItem
*menu_item
;
1780 g_return_if_fail (container
!= NULL
);
1781 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container
));
1782 g_return_if_fail (callback
!= NULL
);
1784 menu_item
= GTK_PIXMAP_MENU_ITEM (container
);
1786 if (menu_item
->pixmap
)
1787 (* callback
) (menu_item
->pixmap
, callback_data
);
1789 GTK_CONTAINER_CLASS(parent_class
)->forall(container
,include_internals
,
1790 callback
,callback_data
);
1794 gtk_pixmap_menu_item_size_request (GtkWidget
*widget
,
1795 GtkRequisition
*requisition
)
1797 GtkPixmapMenuItem
*menu_item
;
1798 GtkRequisition req
= {0, 0};
1800 g_return_if_fail (widget
!= NULL
);
1801 g_return_if_fail (GTK_IS_MENU_ITEM (widget
));
1802 g_return_if_fail (requisition
!= NULL
);
1804 GTK_WIDGET_CLASS(parent_class
)->size_request(widget
,requisition
);
1806 menu_item
= GTK_PIXMAP_MENU_ITEM (widget
);
1808 if (menu_item
->pixmap
)
1809 gtk_widget_size_request(menu_item
->pixmap
, &req
);
1811 requisition
->height
= MAX(req
.height
+ GTK_CONTAINER(widget
)->border_width
+ BORDER_SPACING
, (unsigned int) requisition
->height
);
1812 requisition
->width
+= (req
.width
+ GTK_CONTAINER(widget
)->border_width
+ BORDER_SPACING
);
1816 gtk_pixmap_menu_item_remove (GtkContainer
*container
,
1820 gboolean widget_was_visible
;
1822 g_return_if_fail (container
!= NULL
);
1823 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container
));
1824 g_return_if_fail (child
!= NULL
);
1825 g_return_if_fail (GTK_IS_WIDGET (child
));
1827 bin
= GTK_BIN (container
);
1828 g_return_if_fail ((bin
->child
== child
||
1829 (GTK_PIXMAP_MENU_ITEM(container
)->pixmap
== child
)));
1831 widget_was_visible
= GTK_WIDGET_VISIBLE (child
);
1833 gtk_widget_unparent (child
);
1834 if (bin
->child
== child
)
1837 GTK_PIXMAP_MENU_ITEM(container
)->pixmap
= NULL
;
1838 changed_have_pixmap_status(GTK_PIXMAP_MENU_ITEM(container
));
1841 if (widget_was_visible
)
1842 gtk_widget_queue_resize (GTK_WIDGET (container
));
1846 /* important to only call this if there was actually a _change_ in pixmap == NULL */
1848 changed_have_pixmap_status (GtkPixmapMenuItem
*menu_item
)
1850 if (menu_item
->pixmap
!= NULL
) {
1851 GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
+= 1;
1853 if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
== 1) {
1854 /* Install pixmap toggle size */
1855 GTK_MENU_ITEM_GET_CLASS(menu_item
)->toggle_size
= MAX(GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->orig_toggle_size
, PMAP_WIDTH
);
1858 GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
-= 1;
1860 if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
== 0) {
1861 /* Install normal toggle size */
1862 GTK_MENU_ITEM_GET_CLASS(menu_item
)->toggle_size
= GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->orig_toggle_size
;
1866 /* Note that we actually need to do this for _all_ GtkPixmapMenuItem
1867 whenever the klass->toggle_size changes; but by doing it anytime
1868 this function is called, we get the same effect, just because of
1869 how the preferences option to show pixmaps works. Bogus, broken.
1871 if (GTK_WIDGET_VISIBLE(GTK_WIDGET(menu_item
)))
1872 gtk_widget_queue_resize(GTK_WIDGET(menu_item
));
1875 #endif // USE_MENU_BITMAPS