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 guint accel_key
= gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV( text
) );
1202 gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (label
), menuItem
);
1203 if (accel_key
!= GDK_VoidSymbol
)
1205 gtk_widget_add_accelerator (menuItem
,
1207 gtk_menu_ensure_uline_accel_group (GTK_MENU (m_menu
)),
1211 gtk_widget_show (label
);
1213 mitem
->SetLabelWidget(label
);
1215 GtkWidget
* pixmap
= gtk_pixmap_new( bitmap
->GetPixmap(), bitmap
->GetMask() ? bitmap
->GetMask()->GetBitmap() : (GdkBitmap
* )NULL
);
1216 gtk_widget_show(pixmap
);
1217 gtk_pixmap_menu_item_set_pixmap(GTK_PIXMAP_MENU_ITEM( menuItem
), pixmap
);
1219 gtk_signal_connect( GTK_OBJECT(menuItem
), "activate",
1220 GTK_SIGNAL_FUNC(gtk_menu_clicked_callback
),
1223 gtk_menu_append( GTK_MENU(m_menu
), menuItem
);
1224 gtk_widget_show( menuItem
);
1226 appended
= TRUE
; // We've done this, don't do it again
1228 #endif // USE_MENU_BITMAPS
1229 else // a normal item
1231 // text has "_" instead of "&" after mitem->SetText() so don't use it
1232 wxString
text( mitem
->GetText() );
1234 // buffers containing the menu item path and type in multibyte form
1238 strcpy( bufPath
, "/" );
1239 strncat( bufPath
, wxGTK_CONV(text
), WXSIZEOF(bufPath
) - 2 );
1240 bufPath
[WXSIZEOF(bufPath
) - 1] = '\0';
1242 GtkItemFactoryEntry entry
;
1243 entry
.path
= bufPath
;
1244 entry
.callback
= (GtkItemFactoryCallback
) gtk_menu_clicked_callback
;
1245 entry
.callback_action
= 0;
1248 const char *item_type
;
1249 switch ( mitem
->GetKind() )
1252 item_type
= "<CheckItem>";
1256 if ( m_pathLastRadio
.empty() )
1258 // start of a new radio group
1259 item_type
= "<RadioItem>";
1260 wxString
tmp( wxGTK_CONV_BACK( bufPath
) );
1262 m_pathLastRadio
= tmp
;
1264 else // continue the radio group
1266 pathRadio
= m_pathLastRadio
;
1267 pathRadio
.Replace(wxT("_"), wxT(""));
1268 pathRadio
.Prepend(wxT("<main>/"));
1270 strncpy(bufType
, wxGTK_CONV(pathRadio
), WXSIZEOF(bufType
));
1271 bufType
[WXSIZEOF(bufType
) - 1] = '\0';
1272 item_type
= bufType
;
1275 // continue the existing radio group, if any
1276 endOfRadioGroup
= FALSE
;
1280 wxFAIL_MSG( _T("unexpected menu item kind") );
1284 item_type
= "<Item>";
1288 entry
.item_type
= (char *)item_type
; // cast needed for GTK+
1289 entry
.accelerator
= (gchar
*) NULL
;
1292 // due to an apparent bug in GTK+, we have to use a static buffer here -
1293 // otherwise GTK+ 1.2.2 manages to override the memory we pass to it
1295 char s_accel
[50]; // should be big enough, we check for overruns
1296 wxString
tmp( GetHotKey(*mitem
) );
1297 strncpy(s_accel
, wxGTK_CONV( tmp
), WXSIZEOF(s_accel
));
1298 s_accel
[WXSIZEOF(s_accel
) - 1] = '\0';
1299 entry
.accelerator
= s_accel
;
1300 #else // !wxUSE_ACCEL
1301 entry
.accelerator
= (char*) NULL
;
1302 #endif // wxUSE_ACCEL/!wxUSE_ACCEL
1304 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); /* what is 2 ? */
1306 wxString
path( mitem
->GetFactoryPath() );
1307 menuItem
= gtk_item_factory_get_widget( m_factory
, wxGTK_CONV( path
) );
1310 wxLogError( wxT("Wrong menu path: %s\n"), path
.c_str() );
1313 if ( !mitem
->IsSeparator() )
1315 wxASSERT_MSG( menuItem
, wxT("invalid menuitem") );
1317 gtk_signal_connect( GTK_OBJECT(menuItem
), "select",
1318 GTK_SIGNAL_FUNC(gtk_menu_hilight_callback
),
1321 gtk_signal_connect( GTK_OBJECT(menuItem
), "deselect",
1322 GTK_SIGNAL_FUNC(gtk_menu_nolight_callback
),
1326 mitem
->SetMenuItem(menuItem
);
1328 if ( endOfRadioGroup
)
1330 m_pathLastRadio
.clear();
1336 bool wxMenu::DoAppend(wxMenuItem
*mitem
)
1338 return GtkAppend(mitem
) && wxMenuBase::DoAppend(mitem
);
1341 bool wxMenu::DoInsert(size_t pos
, wxMenuItem
*item
)
1343 if ( !wxMenuBase::DoInsert(pos
, item
) )
1346 // GTK+ doesn't have a function to insert a menu using GtkItemFactory (as
1347 // of version 1.2.6), so we first append the item and then change its
1349 if ( !GtkAppend(item
) )
1352 if ( m_style
& wxMENU_TEAROFF
)
1354 // change the position as the first item is the tear-off marker
1358 GtkMenuShell
*menu_shell
= GTK_MENU_SHELL(m_factory
->widget
);
1359 gpointer data
= g_list_last(menu_shell
->children
)->data
;
1360 menu_shell
->children
= g_list_remove(menu_shell
->children
, data
);
1361 menu_shell
->children
= g_list_insert(menu_shell
->children
, data
, pos
);
1366 wxMenuItem
*wxMenu::DoRemove(wxMenuItem
*item
)
1368 if ( !wxMenuBase::DoRemove(item
) )
1369 return (wxMenuItem
*)NULL
;
1371 // TODO: this code doesn't delete the item factory item and this seems
1372 // impossible as of GTK 1.2.6.
1373 gtk_widget_destroy( item
->GetMenuItem() );
1378 int wxMenu::FindMenuIdByMenuItem( GtkWidget
*menuItem
) const
1380 wxMenuItemList::Node
*node
= m_items
.GetFirst();
1383 wxMenuItem
*item
= node
->GetData();
1384 if (item
->GetMenuItem() == menuItem
)
1385 return item
->GetId();
1386 node
= node
->GetNext();
1392 // ----------------------------------------------------------------------------
1394 // ----------------------------------------------------------------------------
1396 #if GTK_CHECK_VERSION(1, 2, 0) && wxUSE_ACCEL
1398 static wxString
GetHotKey( const wxMenuItem
& item
)
1402 wxAcceleratorEntry
*accel
= item
.GetAccel();
1405 int flags
= accel
->GetFlags();
1406 if ( flags
& wxACCEL_ALT
)
1407 hotkey
+= wxT("<alt>");
1408 if ( flags
& wxACCEL_CTRL
)
1409 hotkey
+= wxT("<control>");
1410 if ( flags
& wxACCEL_SHIFT
)
1411 hotkey
+= wxT("<shift>");
1413 int code
= accel
->GetKeyCode();
1428 hotkey
<< wxT('F') << code
- WXK_F1
+ 1;
1431 // TODO: we should use gdk_keyval_name() (a.k.a.
1432 // XKeysymToString) here as well as hardcoding the keysym
1433 // names this might be not portable
1434 case WXK_NUMPAD_INSERT
:
1435 hotkey
<< wxT("KP_Insert" );
1437 case WXK_NUMPAD_DELETE
:
1438 hotkey
<< wxT("KP_Delete" );
1441 hotkey
<< wxT("Insert" );
1444 hotkey
<< wxT("Delete" );
1447 hotkey
<< wxT("Up" );
1450 hotkey
<< wxT("Down" );
1453 hotkey
<< wxT("Prior" );
1456 hotkey
<< wxT("Next" );
1459 hotkey
<< wxT("Left" );
1462 hotkey
<< wxT("Right" );
1465 hotkey
<< wxT("Home" );
1468 hotkey
<< wxT("End" );
1471 hotkey
<< wxT("Return" );
1474 // if there are any other keys wxGetAccelFromString() may
1475 // return, we should process them here
1480 wxString name
= wxGTK_CONV_BACK( gdk_keyval_name((guint
)code
) );
1488 wxFAIL_MSG( wxT("unknown keyboard accel") );
1497 #endif // wxUSE_ACCEL
1500 //-----------------------------------------------------------------------------
1501 // substitute for missing GtkPixmapMenuItem
1502 //-----------------------------------------------------------------------------
1504 #ifdef USE_MENU_BITMAPS
1507 * Copyright (C) 1998, 1999, 2000 Free Software Foundation
1508 * All rights reserved.
1510 * This file is part of the Gnome Library.
1512 * The Gnome Library is free software; you can redistribute it and/or
1513 * modify it under the terms of the GNU Library General Public License as
1514 * published by the Free Software Foundation; either version 2 of the
1515 * License, or (at your option) any later version.
1517 * The Gnome Library is distributed in the hope that it will be useful,
1518 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1519 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1520 * Library General Public License for more details.
1522 * You should have received a copy of the GNU Library General Public
1523 * License along with the Gnome Library; see the file COPYING.LIB. If not,
1524 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
1525 * Boston, MA 02111-1307, USA.
1531 /* Author: Dietmar Maurer <dm@vlsivie.tuwien.ac.at> */
1533 #include <gtk/gtkaccellabel.h>
1534 #include <gtk/gtksignal.h>
1535 #include <gtk/gtkmenuitem.h>
1536 #include <gtk/gtkmenu.h>
1537 #include <gtk/gtkcontainer.h>
1542 static void gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass
*klass
);
1543 static void gtk_pixmap_menu_item_init (GtkPixmapMenuItem
*menu_item
);
1544 static void gtk_pixmap_menu_item_draw (GtkWidget
*widget
,
1545 GdkRectangle
*area
);
1546 static gint
gtk_pixmap_menu_item_expose (GtkWidget
*widget
,
1547 GdkEventExpose
*event
);
1549 /* we must override the following functions */
1551 static void gtk_pixmap_menu_item_map (GtkWidget
*widget
);
1552 static void gtk_pixmap_menu_item_size_allocate (GtkWidget
*widget
,
1553 GtkAllocation
*allocation
);
1554 static void gtk_pixmap_menu_item_forall (GtkContainer
*container
,
1555 gboolean include_internals
,
1556 GtkCallback callback
,
1557 gpointer callback_data
);
1558 static void gtk_pixmap_menu_item_size_request (GtkWidget
*widget
,
1559 GtkRequisition
*requisition
);
1560 static void gtk_pixmap_menu_item_remove (GtkContainer
*container
,
1563 static void changed_have_pixmap_status (GtkPixmapMenuItem
*menu_item
);
1565 static GtkMenuItemClass
*parent_class
= NULL
;
1569 #define BORDER_SPACING 3
1570 #define PMAP_WIDTH 20
1573 gtk_pixmap_menu_item_get_type (void)
1575 static GtkType pixmap_menu_item_type
= 0;
1577 if (!pixmap_menu_item_type
)
1579 GtkTypeInfo pixmap_menu_item_info
=
1581 (char *)"GtkPixmapMenuItem",
1582 sizeof (GtkPixmapMenuItem
),
1583 sizeof (GtkPixmapMenuItemClass
),
1584 (GtkClassInitFunc
) gtk_pixmap_menu_item_class_init
,
1585 (GtkObjectInitFunc
) gtk_pixmap_menu_item_init
,
1586 /* reserved_1 */ NULL
,
1587 /* reserved_2 */ NULL
,
1588 (GtkClassInitFunc
) NULL
,
1591 pixmap_menu_item_type
= gtk_type_unique (gtk_menu_item_get_type (),
1592 &pixmap_menu_item_info
);
1595 return pixmap_menu_item_type
;
1599 * gtk_pixmap_menu_item_new
1601 * Creates a new pixmap menu item. Use gtk_pixmap_menu_item_set_pixmap()
1602 * to set the pixmap wich is displayed at the left side.
1605 * &GtkWidget pointer to new menu item
1609 gtk_pixmap_menu_item_new (void)
1611 return GTK_WIDGET (gtk_type_new (gtk_pixmap_menu_item_get_type ()));
1615 gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass
*klass
)
1617 GtkObjectClass
*object_class
;
1618 GtkWidgetClass
*widget_class
;
1619 GtkMenuItemClass
*menu_item_class
;
1620 GtkContainerClass
*container_class
;
1622 object_class
= (GtkObjectClass
*) klass
;
1623 widget_class
= (GtkWidgetClass
*) klass
;
1624 menu_item_class
= (GtkMenuItemClass
*) klass
;
1625 container_class
= (GtkContainerClass
*) klass
;
1627 parent_class
= (GtkMenuItemClass
*) gtk_type_class (gtk_menu_item_get_type ());
1629 widget_class
->draw
= gtk_pixmap_menu_item_draw
;
1630 widget_class
->expose_event
= gtk_pixmap_menu_item_expose
;
1631 widget_class
->map
= gtk_pixmap_menu_item_map
;
1632 widget_class
->size_allocate
= gtk_pixmap_menu_item_size_allocate
;
1633 widget_class
->size_request
= gtk_pixmap_menu_item_size_request
;
1635 container_class
->forall
= gtk_pixmap_menu_item_forall
;
1636 container_class
->remove
= gtk_pixmap_menu_item_remove
;
1638 klass
->orig_toggle_size
= menu_item_class
->toggle_size
;
1639 klass
->have_pixmap_count
= 0;
1643 gtk_pixmap_menu_item_init (GtkPixmapMenuItem
*menu_item
)
1647 mi
= GTK_MENU_ITEM (menu_item
);
1649 menu_item
->pixmap
= NULL
;
1653 gtk_pixmap_menu_item_draw (GtkWidget
*widget
,
1656 g_return_if_fail (widget
!= NULL
);
1657 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
));
1658 g_return_if_fail (area
!= NULL
);
1660 if (GTK_WIDGET_CLASS (parent_class
)->draw
)
1661 (* GTK_WIDGET_CLASS (parent_class
)->draw
) (widget
, area
);
1663 if (GTK_WIDGET_DRAWABLE (widget
) &&
1664 GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
) {
1665 gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
),NULL
);
1670 gtk_pixmap_menu_item_expose (GtkWidget
*widget
,
1671 GdkEventExpose
*event
)
1673 g_return_val_if_fail (widget
!= NULL
, FALSE
);
1674 g_return_val_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
), FALSE
);
1675 g_return_val_if_fail (event
!= NULL
, FALSE
);
1677 if (GTK_WIDGET_CLASS (parent_class
)->expose_event
)
1678 (* GTK_WIDGET_CLASS (parent_class
)->expose_event
) (widget
, event
);
1680 if (GTK_WIDGET_DRAWABLE (widget
) &&
1681 GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
) {
1682 gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
),NULL
);
1689 * gtk_pixmap_menu_item_set_pixmap
1690 * @menu_item: Pointer to the pixmap menu item
1691 * @pixmap: Pointer to a pixmap widget
1693 * Set the pixmap of the menu item.
1698 gtk_pixmap_menu_item_set_pixmap (GtkPixmapMenuItem
*menu_item
,
1701 g_return_if_fail (menu_item
!= NULL
);
1702 g_return_if_fail (pixmap
!= NULL
);
1703 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (menu_item
));
1704 g_return_if_fail (GTK_IS_WIDGET (pixmap
));
1705 g_return_if_fail (menu_item
->pixmap
== NULL
);
1707 gtk_widget_set_parent (pixmap
, GTK_WIDGET (menu_item
));
1708 menu_item
->pixmap
= pixmap
;
1710 if (GTK_WIDGET_REALIZED (pixmap
->parent
) &&
1711 !GTK_WIDGET_REALIZED (pixmap
))
1712 gtk_widget_realize (pixmap
);
1714 if (GTK_WIDGET_VISIBLE (pixmap
->parent
)) {
1715 if (GTK_WIDGET_MAPPED (pixmap
->parent
) &&
1716 GTK_WIDGET_VISIBLE(pixmap
) &&
1717 !GTK_WIDGET_MAPPED (pixmap
))
1718 gtk_widget_map (pixmap
);
1721 changed_have_pixmap_status(menu_item
);
1723 if (GTK_WIDGET_VISIBLE (pixmap
) && GTK_WIDGET_VISIBLE (menu_item
))
1724 gtk_widget_queue_resize (pixmap
);
1728 gtk_pixmap_menu_item_map (GtkWidget
*widget
)
1730 GtkPixmapMenuItem
*menu_item
;
1732 g_return_if_fail (widget
!= NULL
);
1733 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
));
1735 menu_item
= GTK_PIXMAP_MENU_ITEM(widget
);
1737 GTK_WIDGET_CLASS(parent_class
)->map(widget
);
1739 if (menu_item
->pixmap
&&
1740 GTK_WIDGET_VISIBLE (menu_item
->pixmap
) &&
1741 !GTK_WIDGET_MAPPED (menu_item
->pixmap
))
1742 gtk_widget_map (menu_item
->pixmap
);
1746 gtk_pixmap_menu_item_size_allocate (GtkWidget
*widget
,
1747 GtkAllocation
*allocation
)
1749 GtkPixmapMenuItem
*pmenu_item
;
1751 pmenu_item
= GTK_PIXMAP_MENU_ITEM(widget
);
1753 if (pmenu_item
->pixmap
&& GTK_WIDGET_VISIBLE(pmenu_item
))
1755 GtkAllocation child_allocation
;
1758 border_width
= GTK_CONTAINER (widget
)->border_width
;
1760 child_allocation
.width
= pmenu_item
->pixmap
->requisition
.width
;
1761 child_allocation
.height
= pmenu_item
->pixmap
->requisition
.height
;
1762 child_allocation
.x
= border_width
+ BORDER_SPACING
;
1763 child_allocation
.y
= (border_width
+ BORDER_SPACING
1764 + (((allocation
->height
- child_allocation
.height
) - child_allocation
.x
)
1765 / 2)); /* center pixmaps vertically */
1766 gtk_widget_size_allocate (pmenu_item
->pixmap
, &child_allocation
);
1769 if (GTK_WIDGET_CLASS (parent_class
)->size_allocate
)
1770 GTK_WIDGET_CLASS(parent_class
)->size_allocate (widget
, allocation
);
1774 gtk_pixmap_menu_item_forall (GtkContainer
*container
,
1775 gboolean include_internals
,
1776 GtkCallback callback
,
1777 gpointer callback_data
)
1779 GtkPixmapMenuItem
*menu_item
;
1781 g_return_if_fail (container
!= NULL
);
1782 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container
));
1783 g_return_if_fail (callback
!= NULL
);
1785 menu_item
= GTK_PIXMAP_MENU_ITEM (container
);
1787 if (menu_item
->pixmap
)
1788 (* callback
) (menu_item
->pixmap
, callback_data
);
1790 GTK_CONTAINER_CLASS(parent_class
)->forall(container
,include_internals
,
1791 callback
,callback_data
);
1795 gtk_pixmap_menu_item_size_request (GtkWidget
*widget
,
1796 GtkRequisition
*requisition
)
1798 GtkPixmapMenuItem
*menu_item
;
1799 GtkRequisition req
= {0, 0};
1801 g_return_if_fail (widget
!= NULL
);
1802 g_return_if_fail (GTK_IS_MENU_ITEM (widget
));
1803 g_return_if_fail (requisition
!= NULL
);
1805 GTK_WIDGET_CLASS(parent_class
)->size_request(widget
,requisition
);
1807 menu_item
= GTK_PIXMAP_MENU_ITEM (widget
);
1809 if (menu_item
->pixmap
)
1810 gtk_widget_size_request(menu_item
->pixmap
, &req
);
1812 requisition
->height
= MAX(req
.height
+ GTK_CONTAINER(widget
)->border_width
+ BORDER_SPACING
, (unsigned int) requisition
->height
);
1813 requisition
->width
+= (req
.width
+ GTK_CONTAINER(widget
)->border_width
+ BORDER_SPACING
);
1817 gtk_pixmap_menu_item_remove (GtkContainer
*container
,
1821 gboolean widget_was_visible
;
1823 g_return_if_fail (container
!= NULL
);
1824 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container
));
1825 g_return_if_fail (child
!= NULL
);
1826 g_return_if_fail (GTK_IS_WIDGET (child
));
1828 bin
= GTK_BIN (container
);
1829 g_return_if_fail ((bin
->child
== child
||
1830 (GTK_PIXMAP_MENU_ITEM(container
)->pixmap
== child
)));
1832 widget_was_visible
= GTK_WIDGET_VISIBLE (child
);
1834 gtk_widget_unparent (child
);
1835 if (bin
->child
== child
)
1838 GTK_PIXMAP_MENU_ITEM(container
)->pixmap
= NULL
;
1839 changed_have_pixmap_status(GTK_PIXMAP_MENU_ITEM(container
));
1842 if (widget_was_visible
)
1843 gtk_widget_queue_resize (GTK_WIDGET (container
));
1847 /* important to only call this if there was actually a _change_ in pixmap == NULL */
1849 changed_have_pixmap_status (GtkPixmapMenuItem
*menu_item
)
1851 if (menu_item
->pixmap
!= NULL
) {
1852 GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
+= 1;
1854 if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
== 1) {
1855 /* Install pixmap toggle size */
1856 GTK_MENU_ITEM_GET_CLASS(menu_item
)->toggle_size
= MAX(GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->orig_toggle_size
, PMAP_WIDTH
);
1859 GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
-= 1;
1861 if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
== 0) {
1862 /* Install normal toggle size */
1863 GTK_MENU_ITEM_GET_CLASS(menu_item
)->toggle_size
= GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->orig_toggle_size
;
1867 /* Note that we actually need to do this for _all_ GtkPixmapMenuItem
1868 whenever the klass->toggle_size changes; but by doing it anytime
1869 this function is called, we get the same effect, just because of
1870 how the preferences option to show pixmaps works. Bogus, broken.
1872 if (GTK_WIDGET_VISIBLE(GTK_WIDGET(menu_item
)))
1873 gtk_widget_queue_resize(GTK_WIDGET(menu_item
));
1876 #endif // USE_MENU_BITMAPS