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 if ( GTK_IS_WIDGET( m_menu
))
1128 gtk_widget_destroy( m_menu
);
1130 gtk_object_unref( GTK_OBJECT(m_factory
) );
1133 bool wxMenu::GtkAppend(wxMenuItem
*mitem
)
1135 GtkWidget
*menuItem
;
1137 #if defined(USE_MENU_BITMAPS) || !GTK_CHECK_VERSION(1, 2, 0)
1138 bool appended
= FALSE
;
1141 // does this item terminate the current radio group?
1142 bool endOfRadioGroup
= TRUE
;
1144 if ( mitem
->IsSeparator() )
1146 GtkItemFactoryEntry entry
;
1147 entry
.path
= (char *)"/sep";
1148 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
1149 entry
.callback_action
= 0;
1150 entry
.item_type
= (char *)"<Separator>";
1151 entry
.accelerator
= (gchar
*) NULL
;
1153 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
1155 // this will be wrong for more than one separator. do we care?
1156 menuItem
= gtk_item_factory_get_widget( m_factory
, "<main>/sep" );
1158 // we might have a separator inside a radio group
1159 endOfRadioGroup
= FALSE
;
1161 else if ( mitem
->IsSubMenu() )
1163 // text has "_" instead of "&" after mitem->SetText()
1164 wxString
text( mitem
->GetText() );
1166 // local buffer in multibyte form
1169 strcat( buf
, wxGTK_CONV( text
) );
1171 GtkItemFactoryEntry entry
;
1173 entry
.callback
= (GtkItemFactoryCallback
) 0;
1174 entry
.callback_action
= 0;
1175 entry
.item_type
= (char *)"<Branch>";
1176 entry
.accelerator
= (gchar
*) NULL
;
1178 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
1180 wxString
path( mitem
->GetFactoryPath() );
1181 menuItem
= gtk_item_factory_get_item( m_factory
, wxGTK_CONV( path
) );
1183 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menuItem
), mitem
->GetSubMenu()->m_menu
);
1185 // if adding a submenu to a menu already existing in the menu bar, we
1186 // must set invoking window to allow processing events from this
1188 if ( m_invokingWindow
)
1189 wxMenubarSetInvokingWindow(mitem
->GetSubMenu(), m_invokingWindow
);
1191 #ifdef USE_MENU_BITMAPS
1192 else if (mitem
->GetBitmap().Ok()) // An item with bitmap
1194 wxString
text( mitem
->GetText() );
1195 const wxBitmap
*bitmap
= &mitem
->GetBitmap();
1197 menuItem
= gtk_pixmap_menu_item_new ();
1198 GtkWidget
*label
= gtk_accel_label_new ( wxGTK_CONV( text
) );
1199 gtk_misc_set_alignment (GTK_MISC (label
), 0.0, 0.5);
1200 gtk_container_add (GTK_CONTAINER (menuItem
), label
);
1201 gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (label
), menuItem
);
1203 GdkModifierType accel_mods
;
1205 // accelerator for the item, as specified by its label
1206 // (ex. Ctrl+O for open)
1207 gtk_accelerator_parse(GetHotKey(*mitem
).c_str(),
1208 &accel_key
, &accel_mods
);
1209 if (accel_key
!= GDK_VoidSymbol
)
1211 gtk_widget_add_accelerator (menuItem
,
1213 gtk_menu_get_accel_group(
1215 accel_key
, accel_mods
,
1219 // accelerator for the underlined char (ex ALT+F for the File menu)
1220 accel_key
= gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV( text
) );
1221 if (accel_key
!= GDK_VoidSymbol
)
1223 gtk_widget_add_accelerator (menuItem
,
1225 gtk_menu_ensure_uline_accel_group (
1231 gtk_widget_show (label
);
1233 mitem
->SetLabelWidget(label
);
1235 GtkWidget
* pixmap
= gtk_pixmap_new( bitmap
->GetPixmap(), bitmap
->GetMask() ? bitmap
->GetMask()->GetBitmap() : (GdkBitmap
* )NULL
);
1236 gtk_widget_show(pixmap
);
1237 gtk_pixmap_menu_item_set_pixmap(GTK_PIXMAP_MENU_ITEM( menuItem
), pixmap
);
1239 gtk_signal_connect( GTK_OBJECT(menuItem
), "activate",
1240 GTK_SIGNAL_FUNC(gtk_menu_clicked_callback
),
1243 gtk_menu_append( GTK_MENU(m_menu
), menuItem
);
1245 gtk_widget_show( menuItem
);
1247 appended
= TRUE
; // We've done this, don't do it again
1249 #endif // USE_MENU_BITMAPS
1250 else // a normal item
1252 // text has "_" instead of "&" after mitem->SetText() so don't use it
1253 wxString
text( mitem
->GetText() );
1255 // buffers containing the menu item path and type in multibyte form
1259 strcpy( bufPath
, "/" );
1260 strncat( bufPath
, wxGTK_CONV(text
), WXSIZEOF(bufPath
) - 2 );
1261 bufPath
[WXSIZEOF(bufPath
) - 1] = '\0';
1263 GtkItemFactoryEntry entry
;
1264 entry
.path
= bufPath
;
1265 entry
.callback
= (GtkItemFactoryCallback
) gtk_menu_clicked_callback
;
1266 entry
.callback_action
= 0;
1269 const char *item_type
;
1270 switch ( mitem
->GetKind() )
1273 item_type
= "<CheckItem>";
1277 if ( m_pathLastRadio
.empty() )
1279 // start of a new radio group
1280 item_type
= "<RadioItem>";
1281 wxString
tmp( wxGTK_CONV_BACK( bufPath
) );
1283 m_pathLastRadio
= tmp
;
1285 else // continue the radio group
1287 pathRadio
= m_pathLastRadio
;
1288 pathRadio
.Replace(wxT("_"), wxT(""));
1289 pathRadio
.Prepend(wxT("<main>/"));
1291 strncpy(bufType
, wxGTK_CONV(pathRadio
), WXSIZEOF(bufType
));
1292 bufType
[WXSIZEOF(bufType
) - 1] = '\0';
1293 item_type
= bufType
;
1296 // continue the existing radio group, if any
1297 endOfRadioGroup
= FALSE
;
1301 wxFAIL_MSG( _T("unexpected menu item kind") );
1305 item_type
= "<Item>";
1309 entry
.item_type
= (char *)item_type
; // cast needed for GTK+
1310 entry
.accelerator
= (gchar
*) NULL
;
1313 // due to an apparent bug in GTK+, we have to use a static buffer here -
1314 // otherwise GTK+ 1.2.2 manages to override the memory we pass to it
1316 char s_accel
[50]; // should be big enough, we check for overruns
1317 wxString
tmp( GetHotKey(*mitem
) );
1318 strncpy(s_accel
, wxGTK_CONV( tmp
), WXSIZEOF(s_accel
));
1319 s_accel
[WXSIZEOF(s_accel
) - 1] = '\0';
1320 entry
.accelerator
= s_accel
;
1321 #else // !wxUSE_ACCEL
1322 entry
.accelerator
= (char*) NULL
;
1323 #endif // wxUSE_ACCEL/!wxUSE_ACCEL
1325 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); /* what is 2 ? */
1327 wxString
path( mitem
->GetFactoryPath() );
1328 menuItem
= gtk_item_factory_get_widget( m_factory
, wxGTK_CONV( path
) );
1331 wxLogError( wxT("Wrong menu path: %s\n"), path
.c_str() );
1334 if ( !mitem
->IsSeparator() )
1336 wxASSERT_MSG( menuItem
, wxT("invalid menuitem") );
1338 gtk_signal_connect( GTK_OBJECT(menuItem
), "select",
1339 GTK_SIGNAL_FUNC(gtk_menu_hilight_callback
),
1342 gtk_signal_connect( GTK_OBJECT(menuItem
), "deselect",
1343 GTK_SIGNAL_FUNC(gtk_menu_nolight_callback
),
1347 mitem
->SetMenuItem(menuItem
);
1349 if ( endOfRadioGroup
)
1351 m_pathLastRadio
.clear();
1357 bool wxMenu::DoAppend(wxMenuItem
*mitem
)
1359 return GtkAppend(mitem
) && wxMenuBase::DoAppend(mitem
);
1362 bool wxMenu::DoInsert(size_t pos
, wxMenuItem
*item
)
1364 if ( !wxMenuBase::DoInsert(pos
, item
) )
1367 // GTK+ doesn't have a function to insert a menu using GtkItemFactory (as
1368 // of version 1.2.6), so we first append the item and then change its
1370 if ( !GtkAppend(item
) )
1373 if ( m_style
& wxMENU_TEAROFF
)
1375 // change the position as the first item is the tear-off marker
1379 GtkMenuShell
*menu_shell
= GTK_MENU_SHELL(m_factory
->widget
);
1380 gpointer data
= g_list_last(menu_shell
->children
)->data
;
1381 menu_shell
->children
= g_list_remove(menu_shell
->children
, data
);
1382 menu_shell
->children
= g_list_insert(menu_shell
->children
, data
, pos
);
1387 wxMenuItem
*wxMenu::DoRemove(wxMenuItem
*item
)
1389 if ( !wxMenuBase::DoRemove(item
) )
1390 return (wxMenuItem
*)NULL
;
1392 // TODO: this code doesn't delete the item factory item and this seems
1393 // impossible as of GTK 1.2.6.
1394 gtk_widget_destroy( item
->GetMenuItem() );
1399 int wxMenu::FindMenuIdByMenuItem( GtkWidget
*menuItem
) const
1401 wxMenuItemList::Node
*node
= m_items
.GetFirst();
1404 wxMenuItem
*item
= node
->GetData();
1405 if (item
->GetMenuItem() == menuItem
)
1406 return item
->GetId();
1407 node
= node
->GetNext();
1413 // ----------------------------------------------------------------------------
1415 // ----------------------------------------------------------------------------
1417 #if GTK_CHECK_VERSION(1, 2, 0) && wxUSE_ACCEL
1419 static wxString
GetHotKey( const wxMenuItem
& item
)
1423 wxAcceleratorEntry
*accel
= item
.GetAccel();
1426 int flags
= accel
->GetFlags();
1427 if ( flags
& wxACCEL_ALT
)
1428 hotkey
+= wxT("<alt>");
1429 if ( flags
& wxACCEL_CTRL
)
1430 hotkey
+= wxT("<control>");
1431 if ( flags
& wxACCEL_SHIFT
)
1432 hotkey
+= wxT("<shift>");
1434 int code
= accel
->GetKeyCode();
1449 hotkey
<< wxT('F') << code
- WXK_F1
+ 1;
1452 // TODO: we should use gdk_keyval_name() (a.k.a.
1453 // XKeysymToString) here as well as hardcoding the keysym
1454 // names this might be not portable
1455 case WXK_NUMPAD_INSERT
:
1456 hotkey
<< wxT("KP_Insert" );
1458 case WXK_NUMPAD_DELETE
:
1459 hotkey
<< wxT("KP_Delete" );
1462 hotkey
<< wxT("Insert" );
1465 hotkey
<< wxT("Delete" );
1468 hotkey
<< wxT("Up" );
1471 hotkey
<< wxT("Down" );
1474 hotkey
<< wxT("Prior" );
1477 hotkey
<< wxT("Next" );
1480 hotkey
<< wxT("Left" );
1483 hotkey
<< wxT("Right" );
1486 hotkey
<< wxT("Home" );
1489 hotkey
<< wxT("End" );
1492 hotkey
<< wxT("Return" );
1495 // if there are any other keys wxGetAccelFromString() may
1496 // return, we should process them here
1501 wxString name
= wxGTK_CONV_BACK( gdk_keyval_name((guint
)code
) );
1509 wxFAIL_MSG( wxT("unknown keyboard accel") );
1518 #endif // wxUSE_ACCEL
1521 //-----------------------------------------------------------------------------
1522 // substitute for missing GtkPixmapMenuItem
1523 //-----------------------------------------------------------------------------
1525 #ifdef USE_MENU_BITMAPS
1528 * Copyright (C) 1998, 1999, 2000 Free Software Foundation
1529 * All rights reserved.
1531 * This file is part of the Gnome Library.
1533 * The Gnome Library is free software; you can redistribute it and/or
1534 * modify it under the terms of the GNU Library General Public License as
1535 * published by the Free Software Foundation; either version 2 of the
1536 * License, or (at your option) any later version.
1538 * The Gnome Library is distributed in the hope that it will be useful,
1539 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1540 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1541 * Library General Public License for more details.
1543 * You should have received a copy of the GNU Library General Public
1544 * License along with the Gnome Library; see the file COPYING.LIB. If not,
1545 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
1546 * Boston, MA 02111-1307, USA.
1552 /* Author: Dietmar Maurer <dm@vlsivie.tuwien.ac.at> */
1554 #include <gtk/gtkaccellabel.h>
1555 #include <gtk/gtksignal.h>
1556 #include <gtk/gtkmenuitem.h>
1557 #include <gtk/gtkmenu.h>
1558 #include <gtk/gtkcontainer.h>
1563 static void gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass
*klass
);
1564 static void gtk_pixmap_menu_item_init (GtkPixmapMenuItem
*menu_item
);
1565 static void gtk_pixmap_menu_item_draw (GtkWidget
*widget
,
1566 GdkRectangle
*area
);
1567 static gint
gtk_pixmap_menu_item_expose (GtkWidget
*widget
,
1568 GdkEventExpose
*event
);
1570 /* we must override the following functions */
1572 static void gtk_pixmap_menu_item_map (GtkWidget
*widget
);
1573 static void gtk_pixmap_menu_item_size_allocate (GtkWidget
*widget
,
1574 GtkAllocation
*allocation
);
1575 static void gtk_pixmap_menu_item_forall (GtkContainer
*container
,
1576 gboolean include_internals
,
1577 GtkCallback callback
,
1578 gpointer callback_data
);
1579 static void gtk_pixmap_menu_item_size_request (GtkWidget
*widget
,
1580 GtkRequisition
*requisition
);
1581 static void gtk_pixmap_menu_item_remove (GtkContainer
*container
,
1584 static void changed_have_pixmap_status (GtkPixmapMenuItem
*menu_item
);
1586 static GtkMenuItemClass
*parent_class
= NULL
;
1590 #define BORDER_SPACING 3
1591 #define PMAP_WIDTH 20
1594 gtk_pixmap_menu_item_get_type (void)
1596 static GtkType pixmap_menu_item_type
= 0;
1598 if (!pixmap_menu_item_type
)
1600 GtkTypeInfo pixmap_menu_item_info
=
1602 (char *)"GtkPixmapMenuItem",
1603 sizeof (GtkPixmapMenuItem
),
1604 sizeof (GtkPixmapMenuItemClass
),
1605 (GtkClassInitFunc
) gtk_pixmap_menu_item_class_init
,
1606 (GtkObjectInitFunc
) gtk_pixmap_menu_item_init
,
1607 /* reserved_1 */ NULL
,
1608 /* reserved_2 */ NULL
,
1609 (GtkClassInitFunc
) NULL
,
1612 pixmap_menu_item_type
= gtk_type_unique (gtk_menu_item_get_type (),
1613 &pixmap_menu_item_info
);
1616 return pixmap_menu_item_type
;
1620 * gtk_pixmap_menu_item_new
1622 * Creates a new pixmap menu item. Use gtk_pixmap_menu_item_set_pixmap()
1623 * to set the pixmap wich is displayed at the left side.
1626 * &GtkWidget pointer to new menu item
1630 gtk_pixmap_menu_item_new (void)
1632 return GTK_WIDGET (gtk_type_new (gtk_pixmap_menu_item_get_type ()));
1636 gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass
*klass
)
1638 GtkObjectClass
*object_class
;
1639 GtkWidgetClass
*widget_class
;
1640 GtkMenuItemClass
*menu_item_class
;
1641 GtkContainerClass
*container_class
;
1643 object_class
= (GtkObjectClass
*) klass
;
1644 widget_class
= (GtkWidgetClass
*) klass
;
1645 menu_item_class
= (GtkMenuItemClass
*) klass
;
1646 container_class
= (GtkContainerClass
*) klass
;
1648 parent_class
= (GtkMenuItemClass
*) gtk_type_class (gtk_menu_item_get_type ());
1650 widget_class
->draw
= gtk_pixmap_menu_item_draw
;
1651 widget_class
->expose_event
= gtk_pixmap_menu_item_expose
;
1652 widget_class
->map
= gtk_pixmap_menu_item_map
;
1653 widget_class
->size_allocate
= gtk_pixmap_menu_item_size_allocate
;
1654 widget_class
->size_request
= gtk_pixmap_menu_item_size_request
;
1656 container_class
->forall
= gtk_pixmap_menu_item_forall
;
1657 container_class
->remove
= gtk_pixmap_menu_item_remove
;
1659 klass
->orig_toggle_size
= menu_item_class
->toggle_size
;
1660 klass
->have_pixmap_count
= 0;
1664 gtk_pixmap_menu_item_init (GtkPixmapMenuItem
*menu_item
)
1668 mi
= GTK_MENU_ITEM (menu_item
);
1670 menu_item
->pixmap
= NULL
;
1674 gtk_pixmap_menu_item_draw (GtkWidget
*widget
,
1677 g_return_if_fail (widget
!= NULL
);
1678 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
));
1679 g_return_if_fail (area
!= NULL
);
1681 if (GTK_WIDGET_CLASS (parent_class
)->draw
)
1682 (* GTK_WIDGET_CLASS (parent_class
)->draw
) (widget
, area
);
1684 if (GTK_WIDGET_DRAWABLE (widget
) &&
1685 GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
) {
1686 gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
),NULL
);
1691 gtk_pixmap_menu_item_expose (GtkWidget
*widget
,
1692 GdkEventExpose
*event
)
1694 g_return_val_if_fail (widget
!= NULL
, FALSE
);
1695 g_return_val_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
), FALSE
);
1696 g_return_val_if_fail (event
!= NULL
, FALSE
);
1698 if (GTK_WIDGET_CLASS (parent_class
)->expose_event
)
1699 (* GTK_WIDGET_CLASS (parent_class
)->expose_event
) (widget
, event
);
1701 if (GTK_WIDGET_DRAWABLE (widget
) &&
1702 GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
) {
1703 gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
),NULL
);
1710 * gtk_pixmap_menu_item_set_pixmap
1711 * @menu_item: Pointer to the pixmap menu item
1712 * @pixmap: Pointer to a pixmap widget
1714 * Set the pixmap of the menu item.
1719 gtk_pixmap_menu_item_set_pixmap (GtkPixmapMenuItem
*menu_item
,
1722 g_return_if_fail (menu_item
!= NULL
);
1723 g_return_if_fail (pixmap
!= NULL
);
1724 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (menu_item
));
1725 g_return_if_fail (GTK_IS_WIDGET (pixmap
));
1726 g_return_if_fail (menu_item
->pixmap
== NULL
);
1728 gtk_widget_set_parent (pixmap
, GTK_WIDGET (menu_item
));
1729 menu_item
->pixmap
= pixmap
;
1731 if (GTK_WIDGET_REALIZED (pixmap
->parent
) &&
1732 !GTK_WIDGET_REALIZED (pixmap
))
1733 gtk_widget_realize (pixmap
);
1735 if (GTK_WIDGET_VISIBLE (pixmap
->parent
)) {
1736 if (GTK_WIDGET_MAPPED (pixmap
->parent
) &&
1737 GTK_WIDGET_VISIBLE(pixmap
) &&
1738 !GTK_WIDGET_MAPPED (pixmap
))
1739 gtk_widget_map (pixmap
);
1742 changed_have_pixmap_status(menu_item
);
1744 if (GTK_WIDGET_VISIBLE (pixmap
) && GTK_WIDGET_VISIBLE (menu_item
))
1745 gtk_widget_queue_resize (pixmap
);
1749 gtk_pixmap_menu_item_map (GtkWidget
*widget
)
1751 GtkPixmapMenuItem
*menu_item
;
1753 g_return_if_fail (widget
!= NULL
);
1754 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
));
1756 menu_item
= GTK_PIXMAP_MENU_ITEM(widget
);
1758 GTK_WIDGET_CLASS(parent_class
)->map(widget
);
1760 if (menu_item
->pixmap
&&
1761 GTK_WIDGET_VISIBLE (menu_item
->pixmap
) &&
1762 !GTK_WIDGET_MAPPED (menu_item
->pixmap
))
1763 gtk_widget_map (menu_item
->pixmap
);
1767 gtk_pixmap_menu_item_size_allocate (GtkWidget
*widget
,
1768 GtkAllocation
*allocation
)
1770 GtkPixmapMenuItem
*pmenu_item
;
1772 pmenu_item
= GTK_PIXMAP_MENU_ITEM(widget
);
1774 if (pmenu_item
->pixmap
&& GTK_WIDGET_VISIBLE(pmenu_item
))
1776 GtkAllocation child_allocation
;
1779 border_width
= GTK_CONTAINER (widget
)->border_width
;
1781 child_allocation
.width
= pmenu_item
->pixmap
->requisition
.width
;
1782 child_allocation
.height
= pmenu_item
->pixmap
->requisition
.height
;
1783 child_allocation
.x
= border_width
+ BORDER_SPACING
;
1784 child_allocation
.y
= (border_width
+ BORDER_SPACING
1785 + (((allocation
->height
- child_allocation
.height
) - child_allocation
.x
)
1786 / 2)); /* center pixmaps vertically */
1787 gtk_widget_size_allocate (pmenu_item
->pixmap
, &child_allocation
);
1790 if (GTK_WIDGET_CLASS (parent_class
)->size_allocate
)
1791 GTK_WIDGET_CLASS(parent_class
)->size_allocate (widget
, allocation
);
1795 gtk_pixmap_menu_item_forall (GtkContainer
*container
,
1796 gboolean include_internals
,
1797 GtkCallback callback
,
1798 gpointer callback_data
)
1800 GtkPixmapMenuItem
*menu_item
;
1802 g_return_if_fail (container
!= NULL
);
1803 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container
));
1804 g_return_if_fail (callback
!= NULL
);
1806 menu_item
= GTK_PIXMAP_MENU_ITEM (container
);
1808 if (menu_item
->pixmap
)
1809 (* callback
) (menu_item
->pixmap
, callback_data
);
1811 GTK_CONTAINER_CLASS(parent_class
)->forall(container
,include_internals
,
1812 callback
,callback_data
);
1816 gtk_pixmap_menu_item_size_request (GtkWidget
*widget
,
1817 GtkRequisition
*requisition
)
1819 GtkPixmapMenuItem
*menu_item
;
1820 GtkRequisition req
= {0, 0};
1822 g_return_if_fail (widget
!= NULL
);
1823 g_return_if_fail (GTK_IS_MENU_ITEM (widget
));
1824 g_return_if_fail (requisition
!= NULL
);
1826 GTK_WIDGET_CLASS(parent_class
)->size_request(widget
,requisition
);
1828 menu_item
= GTK_PIXMAP_MENU_ITEM (widget
);
1830 if (menu_item
->pixmap
)
1831 gtk_widget_size_request(menu_item
->pixmap
, &req
);
1833 requisition
->height
= MAX(req
.height
+ GTK_CONTAINER(widget
)->border_width
+ BORDER_SPACING
, (unsigned int) requisition
->height
);
1834 requisition
->width
+= (req
.width
+ GTK_CONTAINER(widget
)->border_width
+ BORDER_SPACING
);
1838 gtk_pixmap_menu_item_remove (GtkContainer
*container
,
1842 gboolean widget_was_visible
;
1844 g_return_if_fail (container
!= NULL
);
1845 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container
));
1846 g_return_if_fail (child
!= NULL
);
1847 g_return_if_fail (GTK_IS_WIDGET (child
));
1849 bin
= GTK_BIN (container
);
1850 g_return_if_fail ((bin
->child
== child
||
1851 (GTK_PIXMAP_MENU_ITEM(container
)->pixmap
== child
)));
1853 widget_was_visible
= GTK_WIDGET_VISIBLE (child
);
1855 gtk_widget_unparent (child
);
1856 if (bin
->child
== child
)
1859 GTK_PIXMAP_MENU_ITEM(container
)->pixmap
= NULL
;
1860 changed_have_pixmap_status(GTK_PIXMAP_MENU_ITEM(container
));
1863 if (widget_was_visible
)
1864 gtk_widget_queue_resize (GTK_WIDGET (container
));
1868 /* important to only call this if there was actually a _change_ in pixmap == NULL */
1870 changed_have_pixmap_status (GtkPixmapMenuItem
*menu_item
)
1872 if (menu_item
->pixmap
!= NULL
) {
1873 GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
+= 1;
1875 if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
== 1) {
1876 /* Install pixmap toggle size */
1877 GTK_MENU_ITEM_GET_CLASS(menu_item
)->toggle_size
= MAX(GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->orig_toggle_size
, PMAP_WIDTH
);
1880 GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
-= 1;
1882 if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
== 0) {
1883 /* Install normal toggle size */
1884 GTK_MENU_ITEM_GET_CLASS(menu_item
)->toggle_size
= GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->orig_toggle_size
;
1888 /* Note that we actually need to do this for _all_ GtkPixmapMenuItem
1889 whenever the klass->toggle_size changes; but by doing it anytime
1890 this function is called, we get the same effect, just because of
1891 how the preferences option to show pixmaps works. Bogus, broken.
1893 if (GTK_WIDGET_VISIBLE(GTK_WIDGET(menu_item
)))
1894 gtk_widget_queue_resize(GTK_WIDGET(menu_item
));
1897 #endif // USE_MENU_BITMAPS