1 /////////////////////////////////////////////////////////////////////////////
4 // Author: Robert Roebling
6 // Copyright: (c) 1998 Robert Roebling
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
10 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
11 #pragma implementation "menu.h"
12 #pragma implementation "menuitem.h"
15 // For compilers that support precompilation, includes "wx.h".
16 #include "wx/wxprec.h"
21 #include "wx/bitmap.h"
28 #include "wx/gtk/private.h"
30 #include <gdk/gdkkeysyms.h>
32 // FIXME: is this right? somehow I don't think so (VZ)
34 #include <glib-object.h>
36 #define gtk_accel_group_attach(g, o) _gtk_accel_group_attach((g), (o))
37 #define gtk_accel_group_detach(g, o) _gtk_accel_group_detach((g), (o))
38 #define gtk_menu_ensure_uline_accel_group(m) gtk_menu_get_accel_group(m)
40 #define ACCEL_OBJECT GObject
41 #define ACCEL_OBJECTS(a) (a)->acceleratables
42 #define ACCEL_OBJ_CAST(obj) G_OBJECT(obj)
44 #define ACCEL_OBJECT GtkObject
45 #define ACCEL_OBJECTS(a) (a)->attach_objects
46 #define ACCEL_OBJ_CAST(obj) GTK_OBJECT(obj)
49 //-----------------------------------------------------------------------------
51 //-----------------------------------------------------------------------------
53 extern void wxapp_install_idle_handler();
56 #if GTK_CHECK_VERSION(1, 2, 0) && wxUSE_ACCEL
57 static wxString
GetHotKey( const wxMenuItem
& item
);
60 //-----------------------------------------------------------------------------
61 // substitute for missing GtkPixmapMenuItem
62 //-----------------------------------------------------------------------------
64 // FIXME: I can't make this compile with GTK+ 2.0, disabling for now (VZ)
66 #define USE_MENU_BITMAPS
69 #ifdef USE_MENU_BITMAPS
71 #define GTK_TYPE_PIXMAP_MENU_ITEM (gtk_pixmap_menu_item_get_type ())
72 #define GTK_PIXMAP_MENU_ITEM(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_PIXMAP_MENU_ITEM, GtkPixmapMenuItem))
73 #define GTK_PIXMAP_MENU_ITEM_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_PIXMAP_MENU_ITEM, GtkPixmapMenuItemClass))
74 #define GTK_IS_PIXMAP_MENU_ITEM(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_PIXMAP_MENU_ITEM))
75 #define GTK_IS_PIXMAP_MENU_ITEM_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PIXMAP_MENU_ITEM))
76 //#define GTK_PIXMAP_MENU_ITEM_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_PIXMAP_MENU_ITEM))
77 #define GTK_PIXMAP_MENU_ITEM_GET_CLASS(obj) (GTK_PIXMAP_MENU_ITEM_CLASS( GTK_OBJECT_GET_CLASS(obj)))
79 #ifndef GTK_MENU_ITEM_GET_CLASS
80 #define GTK_MENU_ITEM_GET_CLASS(obj) (GTK_MENU_ITEM_CLASS( GTK_OBJECT_GET_CLASS(obj)))
83 typedef struct _GtkPixmapMenuItem GtkPixmapMenuItem
;
84 typedef struct _GtkPixmapMenuItemClass GtkPixmapMenuItemClass
;
86 struct _GtkPixmapMenuItem
88 GtkMenuItem menu_item
;
93 struct _GtkPixmapMenuItemClass
95 GtkMenuItemClass parent_class
;
97 guint orig_toggle_size
;
98 guint have_pixmap_count
;
102 GtkType
gtk_pixmap_menu_item_get_type (void);
103 GtkWidget
* gtk_pixmap_menu_item_new (void);
104 void gtk_pixmap_menu_item_set_pixmap (GtkPixmapMenuItem
*menu_item
,
107 #endif // USE_MENU_BITMAPS
109 //-----------------------------------------------------------------------------
111 //-----------------------------------------------------------------------------
113 static wxString
wxReplaceUnderscore( const wxString
& title
)
117 /* GTK 1.2 wants to have "_" instead of "&" for accelerators */
120 while (*pc
!= wxT('\0'))
122 if ((*pc
== wxT('&')) && (*(pc
+1) == wxT('&')))
124 // "&" is doubled to indicate "&" instead of accelerator
128 else if (*pc
== wxT('&'))
130 #if GTK_CHECK_VERSION(1, 2, 0)
134 #if GTK_CHECK_VERSION(2, 0, 0)
135 else if (*pc
== wxT('/'))
139 else if (*pc
== wxT('\\'))
143 #elif GTK_CHECK_VERSION(1, 2, 0)
144 else if (*pc
== wxT('/'))
152 if ( *pc
== wxT('_') )
154 // underscores must be doubled to prevent them from being
155 // interpreted as accelerator character prefix by GTK
167 //-----------------------------------------------------------------------------
168 // activate message from GTK
169 //-----------------------------------------------------------------------------
171 static void gtk_menu_open_callback( GtkWidget
*widget
, wxMenu
*menu
)
173 if (g_isIdle
) wxapp_install_idle_handler();
175 wxMenuEvent
event( wxEVT_MENU_OPEN
, -1, menu
);
176 event
.SetEventObject( menu
);
178 wxEvtHandler
* handler
= menu
->GetEventHandler();
179 if (handler
&& handler
->ProcessEvent(event
))
182 wxWindow
*win
= menu
->GetInvokingWindow();
183 if (win
) win
->GetEventHandler()->ProcessEvent( event
);
186 //-----------------------------------------------------------------------------
188 //-----------------------------------------------------------------------------
190 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar
,wxWindow
)
192 wxMenuBar::wxMenuBar( long style
)
194 /* the parent window is known after wxFrame::SetMenu() */
195 m_needParent
= FALSE
;
197 m_invokingWindow
= (wxWindow
*) NULL
;
199 if (!PreCreation( (wxWindow
*) NULL
, wxDefaultPosition
, wxDefaultSize
) ||
200 !CreateBase( (wxWindow
*) NULL
, -1, wxDefaultPosition
, wxDefaultSize
, style
, wxDefaultValidator
, wxT("menubar") ))
202 wxFAIL_MSG( wxT("wxMenuBar creation failed") );
206 /* GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has. */
207 #if GTK_CHECK_VERSION(1, 2, 1)
208 m_accel
= gtk_accel_group_new();
209 m_factory
= gtk_item_factory_new( GTK_TYPE_MENU_BAR
, "<main>", m_accel
);
210 m_menubar
= gtk_item_factory_get_widget( m_factory
, "<main>" );
212 m_menubar
= gtk_menu_bar_new();
215 if (style
& wxMB_DOCKABLE
)
217 m_widget
= gtk_handle_box_new();
218 gtk_container_add( GTK_CONTAINER(m_widget
), GTK_WIDGET(m_menubar
) );
219 gtk_widget_show( GTK_WIDGET(m_menubar
) );
223 m_widget
= GTK_WIDGET(m_menubar
);
231 wxMenuBar::wxMenuBar()
233 /* the parent window is known after wxFrame::SetMenu() */
234 m_needParent
= FALSE
;
236 m_invokingWindow
= (wxWindow
*) NULL
;
238 if (!PreCreation( (wxWindow
*) NULL
, wxDefaultPosition
, wxDefaultSize
) ||
239 !CreateBase( (wxWindow
*) NULL
, -1, wxDefaultPosition
, wxDefaultSize
, 0, wxDefaultValidator
, wxT("menubar") ))
241 wxFAIL_MSG( wxT("wxMenuBar creation failed") );
245 /* GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has. */
246 #if GTK_CHECK_VERSION(1, 2, 1)
247 m_accel
= gtk_accel_group_new();
248 m_factory
= gtk_item_factory_new( GTK_TYPE_MENU_BAR
, "<main>", m_accel
);
249 m_menubar
= gtk_item_factory_get_widget( m_factory
, "<main>" );
251 m_menubar
= gtk_menu_bar_new();
254 m_widget
= GTK_WIDGET(m_menubar
);
261 wxMenuBar::~wxMenuBar()
263 // gtk_object_unref( GTK_OBJECT(m_factory) ); why not ?
266 static void wxMenubarUnsetInvokingWindow( wxMenu
*menu
, wxWindow
*win
)
268 menu
->SetInvokingWindow( (wxWindow
*) NULL
);
270 wxWindow
*top_frame
= win
;
271 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
272 top_frame
= top_frame
->GetParent();
274 /* support for native hot keys */
275 gtk_accel_group_detach( menu
->m_accel
, ACCEL_OBJ_CAST(top_frame
->m_widget
) );
277 wxMenuItemList::compatibility_iterator node
= menu
->GetMenuItems().GetFirst();
280 wxMenuItem
*menuitem
= node
->GetData();
281 if (menuitem
->IsSubMenu())
282 wxMenubarUnsetInvokingWindow( menuitem
->GetSubMenu(), win
);
283 node
= node
->GetNext();
287 static void wxMenubarSetInvokingWindow( wxMenu
*menu
, wxWindow
*win
)
289 menu
->SetInvokingWindow( win
);
291 #if GTK_CHECK_VERSION(1, 2, 1)
292 wxWindow
*top_frame
= win
;
293 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
294 top_frame
= top_frame
->GetParent();
296 /* support for native hot keys */
297 ACCEL_OBJECT
*obj
= ACCEL_OBJ_CAST(top_frame
->m_widget
);
298 if ( !g_slist_find( ACCEL_OBJECTS(menu
->m_accel
), obj
) )
299 gtk_accel_group_attach( menu
->m_accel
, obj
);
300 #endif // GTK+ 1.2.1+
302 wxMenuItemList::compatibility_iterator node
= menu
->GetMenuItems().GetFirst();
305 wxMenuItem
*menuitem
= node
->GetData();
306 if (menuitem
->IsSubMenu())
307 wxMenubarSetInvokingWindow( menuitem
->GetSubMenu(), win
);
308 node
= node
->GetNext();
312 void wxMenuBar::SetInvokingWindow( wxWindow
*win
)
314 m_invokingWindow
= win
;
315 #if GTK_CHECK_VERSION(1, 2, 1)
316 wxWindow
*top_frame
= win
;
317 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
318 top_frame
= top_frame
->GetParent();
320 /* support for native key accelerators indicated by underscroes */
321 ACCEL_OBJECT
*obj
= ACCEL_OBJ_CAST(top_frame
->m_widget
);
322 if ( !g_slist_find( ACCEL_OBJECTS(m_accel
), obj
) )
323 gtk_accel_group_attach( m_accel
, obj
);
324 #endif // GTK+ 1.2.1+
326 wxMenuList::compatibility_iterator node
= m_menus
.GetFirst();
329 wxMenu
*menu
= node
->GetData();
330 wxMenubarSetInvokingWindow( menu
, win
);
331 node
= node
->GetNext();
335 void wxMenuBar::UnsetInvokingWindow( wxWindow
*win
)
337 m_invokingWindow
= (wxWindow
*) NULL
;
338 #if GTK_CHECK_VERSION(1, 2, 1)
339 wxWindow
*top_frame
= win
;
340 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
341 top_frame
= top_frame
->GetParent();
343 // support for native key accelerators indicated by underscroes
344 gtk_accel_group_detach( m_accel
, ACCEL_OBJ_CAST(top_frame
->m_widget
) );
345 #endif // GTK+ 1.2.1+
347 wxMenuList::compatibility_iterator node
= m_menus
.GetFirst();
350 wxMenu
*menu
= node
->GetData();
351 wxMenubarUnsetInvokingWindow( menu
, win
);
352 node
= node
->GetNext();
356 bool wxMenuBar::Append( wxMenu
*menu
, const wxString
&title
)
358 if ( !wxMenuBarBase::Append( menu
, title
) )
361 return GtkAppend(menu
, title
);
364 bool wxMenuBar::GtkAppend(wxMenu
*menu
, const wxString
& title
)
366 wxString
str( wxReplaceUnderscore( title
) );
368 // This doesn't have much effect right now.
369 menu
->SetTitle( str
);
371 // GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has.
372 #if GTK_CHECK_VERSION(1, 2, 1)
375 buf
<< wxT('/') << str
.c_str();
377 // local buffer in multibyte form
379 strcpy(cbuf
, wxGTK_CONV(buf
) );
381 GtkItemFactoryEntry entry
;
382 entry
.path
= (gchar
*)cbuf
; // const_cast
383 entry
.accelerator
= (gchar
*) NULL
;
384 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
385 entry
.callback_action
= 0;
386 entry
.item_type
= (char *)"<Branch>";
388 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
389 // in order to get the pointer to the item we need the item text _without_ underscores
390 wxString tmp
= wxT("<main>/");
392 for ( pc
= str
; *pc
!= wxT('\0'); pc
++ )
394 // contrary to the common sense, we must throw out _all_ underscores,
395 // (i.e. "Hello__World" => "HelloWorld" and not "Hello_World" as we
396 // might naively think). IMHO it's a bug in GTK+ (VZ)
397 while (*pc
== wxT('_'))
401 menu
->m_owner
= gtk_item_factory_get_item( m_factory
, wxGTK_CONV( tmp
) );
402 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu
->m_owner
), menu
->m_menu
);
405 menu
->m_owner
= gtk_menu_item_new_with_label( wxGTK_CONV( str
) );
406 gtk_widget_show( menu
->m_owner
);
407 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu
->m_owner
), menu
->m_menu
);
409 gtk_menu_bar_append( GTK_MENU_BAR(m_menubar
), menu
->m_owner
);
413 gtk_signal_connect( GTK_OBJECT(menu
->m_owner
), "activate",
414 GTK_SIGNAL_FUNC(gtk_menu_open_callback
),
417 // m_invokingWindow is set after wxFrame::SetMenuBar(). This call enables
418 // addings menu later on.
419 if (m_invokingWindow
)
421 wxMenubarSetInvokingWindow( menu
, m_invokingWindow
);
423 // OPTIMISE ME: we should probably cache this, or pass it
424 // directly, but for now this is a minimal
425 // change to validate the new dynamic sizing.
426 // see (and refactor :) similar code in Remove
429 wxFrame
*frame
= wxDynamicCast( m_invokingWindow
, wxFrame
);
432 frame
->UpdateMenuBarSize();
438 bool wxMenuBar::Insert(size_t pos
, wxMenu
*menu
, const wxString
& title
)
440 if ( !wxMenuBarBase::Insert(pos
, menu
, title
) )
443 // GTK+ doesn't have a function to insert a menu using GtkItemFactory (as
444 // of version 1.2.6), so we first append the item and then change its
446 if ( !GtkAppend(menu
, title
) )
449 if (pos
+1 >= m_menus
.GetCount())
452 GtkMenuShell
*menu_shell
= GTK_MENU_SHELL(m_factory
->widget
);
453 gpointer data
= g_list_last(menu_shell
->children
)->data
;
454 menu_shell
->children
= g_list_remove(menu_shell
->children
, data
);
455 menu_shell
->children
= g_list_insert(menu_shell
->children
, data
, pos
);
460 wxMenu
*wxMenuBar::Replace(size_t pos
, wxMenu
*menu
, const wxString
& title
)
462 // remove the old item and insert a new one
463 wxMenu
*menuOld
= Remove(pos
);
464 if ( menuOld
&& !Insert(pos
, menu
, title
) )
466 return (wxMenu
*) NULL
;
469 // either Insert() succeeded or Remove() failed and menuOld is NULL
473 static wxMenu
*CopyMenu (wxMenu
*menu
)
475 wxMenu
*menucopy
= new wxMenu ();
476 wxMenuItemList::compatibility_iterator node
= menu
->GetMenuItems().GetFirst();
479 wxMenuItem
*item
= node
->GetData();
480 int itemid
= item
->GetId();
481 wxString text
= item
->GetText();
482 text
.Replace(wxT("_"), wxT("&"));
483 wxMenu
*submenu
= item
->GetSubMenu();
486 wxMenuItem
* itemcopy
= new wxMenuItem(menucopy
,
488 menu
->GetHelpString(itemid
));
489 itemcopy
->SetBitmap(item
->GetBitmap());
490 itemcopy
->SetCheckable(item
->IsCheckable());
491 menucopy
->Append(itemcopy
);
494 menucopy
->Append (itemid
, text
, CopyMenu(submenu
),
495 menu
->GetHelpString(itemid
));
497 node
= node
->GetNext();
503 wxMenu
*wxMenuBar::Remove(size_t pos
)
505 wxMenu
*menu
= wxMenuBarBase::Remove(pos
);
507 return (wxMenu
*) NULL
;
510 GtkMenuShell *menu_shell = GTK_MENU_SHELL(m_factory->widget);
512 printf( "factory entries before %d\n", (int)g_slist_length(m_factory->items) );
513 printf( "menu shell entries before %d\n", (int)g_list_length( menu_shell->children ) );
516 wxMenu
*menucopy
= CopyMenu( menu
);
518 // unparent calls unref() and that would delete the widget so we raise
519 // the ref count to 2 artificially before invoking unparent.
520 gtk_widget_ref( menu
->m_menu
);
521 gtk_widget_unparent( menu
->m_menu
);
523 gtk_widget_destroy( menu
->m_owner
);
528 printf( "factory entries after %d\n", (int)g_slist_length(m_factory->items) );
529 printf( "menu shell entries after %d\n", (int)g_list_length( menu_shell->children ) );
532 if (m_invokingWindow
)
534 // OPTIMISE ME: see comment in GtkAppend
536 wxFrame
*frame
= wxDynamicCast( m_invokingWindow
, wxFrame
);
539 frame
->UpdateMenuBarSize();
545 static int FindMenuItemRecursive( const wxMenu
*menu
, const wxString
&menuString
, const wxString
&itemString
)
547 if (wxMenuItem::GetLabelFromText(menu
->GetTitle()) == wxMenuItem::GetLabelFromText(menuString
))
549 int res
= menu
->FindItem( itemString
);
550 if (res
!= wxNOT_FOUND
)
554 wxMenuItemList::compatibility_iterator node
= menu
->GetMenuItems().GetFirst();
557 wxMenuItem
*item
= node
->GetData();
558 if (item
->IsSubMenu())
559 return FindMenuItemRecursive(item
->GetSubMenu(), menuString
, itemString
);
561 node
= node
->GetNext();
567 int wxMenuBar::FindMenuItem( const wxString
&menuString
, const wxString
&itemString
) const
569 wxMenuList::compatibility_iterator node
= m_menus
.GetFirst();
572 wxMenu
*menu
= node
->GetData();
573 int res
= FindMenuItemRecursive( menu
, menuString
, itemString
);
576 node
= node
->GetNext();
582 // Find a wxMenuItem using its id. Recurses down into sub-menus
583 static wxMenuItem
* FindMenuItemByIdRecursive(const wxMenu
* menu
, int id
)
585 wxMenuItem
* result
= menu
->FindChildItem(id
);
587 wxMenuItemList::compatibility_iterator node
= menu
->GetMenuItems().GetFirst();
588 while ( node
&& result
== NULL
)
590 wxMenuItem
*item
= node
->GetData();
591 if (item
->IsSubMenu())
593 result
= FindMenuItemByIdRecursive( item
->GetSubMenu(), id
);
595 node
= node
->GetNext();
601 wxMenuItem
* wxMenuBar::FindItem( int id
, wxMenu
**menuForItem
) const
603 wxMenuItem
* result
= 0;
604 wxMenuList::compatibility_iterator node
= m_menus
.GetFirst();
605 while (node
&& result
== 0)
607 wxMenu
*menu
= node
->GetData();
608 result
= FindMenuItemByIdRecursive( menu
, id
);
609 node
= node
->GetNext();
614 *menuForItem
= result
? result
->GetMenu() : (wxMenu
*)NULL
;
620 void wxMenuBar::EnableTop( size_t pos
, bool flag
)
622 wxMenuList::compatibility_iterator node
= m_menus
.Item( pos
);
624 wxCHECK_RET( node
, wxT("menu not found") );
626 wxMenu
* menu
= node
->GetData();
629 gtk_widget_set_sensitive( menu
->m_owner
, flag
);
632 wxString
wxMenuBar::GetLabelTop( size_t pos
) const
634 wxMenuList::compatibility_iterator node
= m_menus
.Item( pos
);
636 wxCHECK_MSG( node
, wxT("invalid"), wxT("menu not found") );
638 wxMenu
* menu
= node
->GetData();
641 wxString
text( menu
->GetTitle() );
642 for ( const wxChar
*pc
= text
.c_str(); *pc
; pc
++ )
644 if ( *pc
== wxT('_') )
646 // '_' is the escape character for GTK+
650 // don't remove ampersands '&' since if we have them in the menu title
651 // it means that they were doubled to indicate "&" instead of accelerator
659 void wxMenuBar::SetLabelTop( size_t pos
, const wxString
& label
)
661 wxMenuList::compatibility_iterator node
= m_menus
.Item( pos
);
663 wxCHECK_RET( node
, wxT("menu not found") );
665 wxMenu
* menu
= node
->GetData();
667 wxString
str( wxReplaceUnderscore( label
) );
669 menu
->SetTitle( str
);
673 GtkLabel
*label
= GTK_LABEL( GTK_BIN(menu
->m_owner
)->child
);
676 gtk_label_set( label
, wxGTK_CONV( str
) );
678 /* reparse key accel */
679 (void)gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV( str
) );
680 gtk_accel_label_refetch( GTK_ACCEL_LABEL(label
) );
685 //-----------------------------------------------------------------------------
687 //-----------------------------------------------------------------------------
689 static void gtk_menu_clicked_callback( GtkWidget
*widget
, wxMenu
*menu
)
692 wxapp_install_idle_handler();
694 int id
= menu
->FindMenuIdByMenuItem(widget
);
696 /* should find it for normal (not popup) menu */
697 wxASSERT_MSG( (id
!= -1) || (menu
->GetInvokingWindow() != NULL
),
698 _T("menu item not found in gtk_menu_clicked_callback") );
700 if (!menu
->IsEnabled(id
))
703 wxMenuItem
* item
= menu
->FindChildItem( id
);
704 wxCHECK_RET( item
, wxT("error in menu item callback") );
706 if (item
->IsCheckable())
708 bool isReallyChecked
= item
->IsChecked(),
709 isInternallyChecked
= item
->wxMenuItemBase::IsChecked();
711 // ensure that the internal state is always consistent with what is
712 // shown on the screen
713 item
->wxMenuItemBase::Check(isReallyChecked
);
715 // we must not report the events for the radio button going up nor the
716 // events resulting from the calls to wxMenuItem::Check()
717 if ( (item
->GetKind() == wxITEM_RADIO
&& !isReallyChecked
) ||
718 (isInternallyChecked
== isReallyChecked
) )
725 // Is this menu on a menubar? (possibly nested)
726 wxFrame
* frame
= NULL
;
728 while ( pm
&& !frame
)
730 if ( pm
->IsAttached() )
731 frame
= pm
->GetMenuBar()->GetFrame();
732 pm
= pm
->GetParent();
735 // FIXME: why do we have to call wxFrame::GetEventHandler() directly here?
736 // normally wxMenu::SendEvent() should be enough, if it doesn't work
737 // in wxGTK then we have a bug in wxMenu::GetInvokingWindow() which
738 // should be fixed instead of working around it here...
741 // If it is attached then let the frame send the event.
742 // Don't call frame->ProcessCommand(id) because it toggles
743 // checkable items and we've already done that above.
744 wxCommandEvent
commandEvent(wxEVT_COMMAND_MENU_SELECTED
, id
);
745 commandEvent
.SetEventObject(frame
);
746 if (item
->IsCheckable())
747 commandEvent
.SetInt(item
->IsChecked());
748 commandEvent
.SetEventObject(menu
);
750 frame
->GetEventHandler()->ProcessEvent(commandEvent
);
754 // otherwise let the menu have it
755 menu
->SendEvent(id
, item
->IsCheckable() ? item
->IsChecked() : -1);
759 //-----------------------------------------------------------------------------
761 //-----------------------------------------------------------------------------
763 static void gtk_menu_hilight_callback( GtkWidget
*widget
, wxMenu
*menu
)
765 if (g_isIdle
) wxapp_install_idle_handler();
767 int id
= menu
->FindMenuIdByMenuItem(widget
);
769 wxASSERT( id
!= -1 ); // should find it!
771 if (!menu
->IsEnabled(id
))
774 wxMenuEvent
event( wxEVT_MENU_HIGHLIGHT
, id
);
775 event
.SetEventObject( menu
);
777 wxEvtHandler
* handler
= menu
->GetEventHandler();
778 if (handler
&& handler
->ProcessEvent(event
))
781 wxWindow
*win
= menu
->GetInvokingWindow();
782 if (win
) win
->GetEventHandler()->ProcessEvent( event
);
785 //-----------------------------------------------------------------------------
787 //-----------------------------------------------------------------------------
789 static void gtk_menu_nolight_callback( GtkWidget
*widget
, wxMenu
*menu
)
791 if (g_isIdle
) wxapp_install_idle_handler();
793 int id
= menu
->FindMenuIdByMenuItem(widget
);
795 wxASSERT( id
!= -1 ); // should find it!
797 if (!menu
->IsEnabled(id
))
800 wxMenuEvent
event( wxEVT_MENU_HIGHLIGHT
, -1 );
801 event
.SetEventObject( menu
);
803 wxEvtHandler
* handler
= menu
->GetEventHandler();
804 if (handler
&& handler
->ProcessEvent(event
))
807 wxWindow
*win
= menu
->GetInvokingWindow();
809 win
->GetEventHandler()->ProcessEvent( event
);
812 //-----------------------------------------------------------------------------
814 //-----------------------------------------------------------------------------
816 IMPLEMENT_DYNAMIC_CLASS(wxMenuItem
, wxObject
)
818 wxMenuItem
*wxMenuItemBase::New(wxMenu
*parentMenu
,
820 const wxString
& name
,
821 const wxString
& help
,
825 return new wxMenuItem(parentMenu
, id
, name
, help
, kind
, subMenu
);
828 wxMenuItem::wxMenuItem(wxMenu
*parentMenu
,
830 const wxString
& text
,
831 const wxString
& help
,
834 : wxMenuItemBase(parentMenu
, id
, text
, help
, kind
, subMenu
)
839 wxMenuItem::wxMenuItem(wxMenu
*parentMenu
,
841 const wxString
& text
,
842 const wxString
& help
,
845 : wxMenuItemBase(parentMenu
, id
, text
, help
,
846 isCheckable
? wxITEM_CHECK
: wxITEM_NORMAL
, subMenu
)
851 void wxMenuItem::Init(const wxString
& text
)
853 m_labelWidget
= (GtkWidget
*) NULL
;
854 m_menuItem
= (GtkWidget
*) NULL
;
859 wxMenuItem::~wxMenuItem()
861 // don't delete menu items, the menus take care of that
864 // return the menu item text without any menu accels
866 wxString
wxMenuItemBase::GetLabelFromText(const wxString
& text
)
870 for ( const wxChar
*pc
= text
.c_str(); *pc
; pc
++ )
872 if ( *pc
== wxT('_') )
874 // GTK 1.2 escapes "xxx_xxx" to "xxx__xxx"
880 #if GTK_CHECK_VERSION(2, 0, 0)
881 if ( *pc
== wxT('\\') )
883 // GTK 2.0 escapes "xxx/xxx" to "xxx\/xxx"
890 if ( (*pc
== wxT('&')) && (*(pc
+1) != wxT('&')) )
893 // "&" is doubled to indicate "&" instead of accelerator
900 // wxPrintf( L"text %s label %s\n", text.c_str(), label.c_str() );
905 void wxMenuItem::SetText( const wxString
& str
)
907 // Some optimization to avoid flicker
908 wxString oldLabel
= m_text
;
909 oldLabel
= wxStripMenuCodes(oldLabel
.BeforeFirst('\t'));
910 oldLabel
.Replace(wxT("_"), wxT(""));
911 wxString label1
= wxStripMenuCodes(str
.BeforeFirst('\t'));
912 if (oldLabel
== label1
)
921 label
= (GtkLabel
*) m_labelWidget
;
923 label
= GTK_LABEL( GTK_BIN(m_menuItem
)->child
);
925 #if GTK_CHECK_VERSION(2, 0, 0)
926 // We have to imitate item_factory_unescape_label here
928 for (size_t n
= 0; n
< m_text
.Len(); n
++)
930 if (m_text
[n
] != wxT('\\'))
934 gtk_label_set_text_with_mnemonic( GTK_LABEL(label
), wxGTK_CONV(tmp
) );
937 gtk_label_set( label
, wxGTK_CONV( m_text
) );
940 (void)gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV(m_text
) );
941 gtk_accel_label_refetch( GTK_ACCEL_LABEL(label
) );
946 // it's valid for this function to be called even if m_menuItem == NULL
947 void wxMenuItem::DoSetText( const wxString
& str
)
949 // '\t' is the deliminator indicating a hot key
951 const wxChar
*pc
= str
;
952 while ( (*pc
!= wxT('\0')) && (*pc
!= wxT('\t')) )
954 if ((*pc
== wxT('&')) && (*(pc
+1) == wxT('&')))
956 // "&" is doubled to indicate "&" instead of accelerator
960 else if (*pc
== wxT('&'))
964 #if GTK_CHECK_VERSION(2, 0, 0)
965 else if ( *pc
== wxT('_') ) // escape underscores
967 // m_text << wxT("__"); doesn't work
970 else if (*pc
== wxT('/')) // we have to escape slashes
972 m_text
<< wxT("\\/");
974 else if (*pc
== wxT('\\')) // we have to double backslashes
976 m_text
<< wxT("\\\\");
979 else if ( *pc
== wxT('_') ) // escape underscores
983 else if (*pc
== wxT('/')) /* we have to filter out slashes ... */
985 m_text
<< wxT('\\'); /* ... and replace them with back slashes */
994 // wxPrintf( L"str %s m_text %s\n", str.c_str(), m_text.c_str() );
1007 wxAcceleratorEntry
*wxMenuItem::GetAccel() const
1012 return (wxAcceleratorEntry
*)NULL
;
1015 // as wxGetAccelFromString() looks for TAB, insert a dummy one here
1017 label
<< wxT('\t') << GetHotKey();
1019 return wxGetAccelFromString(label
);
1022 #endif // wxUSE_ACCEL
1024 void wxMenuItem::Check( bool check
)
1026 wxCHECK_RET( m_menuItem
, wxT("invalid menu item") );
1028 if (check
== m_isChecked
)
1031 wxMenuItemBase::Check( check
);
1033 switch ( GetKind() )
1037 gtk_check_menu_item_set_state( (GtkCheckMenuItem
*)m_menuItem
, (gint
)check
);
1041 wxFAIL_MSG( _T("can't check this item") );
1045 void wxMenuItem::Enable( bool enable
)
1047 wxCHECK_RET( m_menuItem
, wxT("invalid menu item") );
1049 gtk_widget_set_sensitive( m_menuItem
, enable
);
1050 wxMenuItemBase::Enable( enable
);
1053 bool wxMenuItem::IsChecked() const
1055 wxCHECK_MSG( m_menuItem
, FALSE
, wxT("invalid menu item") );
1057 wxCHECK_MSG( IsCheckable(), FALSE
,
1058 wxT("can't get state of uncheckable item!") );
1060 return ((GtkCheckMenuItem
*)m_menuItem
)->active
!= 0;
1063 wxString
wxMenuItem::GetFactoryPath() const
1065 // In order to get the pointer to the item we need the item
1066 // text _without_ underscores in GTK 1.2
1067 wxString
path( wxT("<main>/") );
1069 for ( const wxChar
*pc
= m_text
.c_str(); *pc
; pc
++ )
1071 if ( *pc
== wxT('_') )
1076 // remove '_' unconditionally
1081 // don't remove ampersands '&' since if we have them in the menu item title
1082 // it means that they were doubled to indicate "&" instead of accelerator
1090 //-----------------------------------------------------------------------------
1092 //-----------------------------------------------------------------------------
1094 IMPLEMENT_DYNAMIC_CLASS(wxMenu
,wxEvtHandler
)
1098 m_accel
= gtk_accel_group_new();
1099 m_factory
= gtk_item_factory_new( GTK_TYPE_MENU
, "<main>", m_accel
);
1100 m_menu
= gtk_item_factory_get_widget( m_factory
, "<main>" );
1102 m_owner
= (GtkWidget
*) NULL
;
1104 // Tearoffs are entries, just like separators. So if we want this
1105 // menu to be a tear-off one, we just append a tearoff entry
1107 if(m_style
& wxMENU_TEAROFF
)
1109 GtkItemFactoryEntry entry
;
1110 entry
.path
= (char *)"/tearoff";
1111 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
1112 entry
.callback_action
= 0;
1113 entry
.item_type
= (char *)"<Tearoff>";
1114 entry
.accelerator
= (gchar
*) NULL
;
1115 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
1116 //GtkWidget *menuItem = gtk_item_factory_get_widget( m_factory, "<main>/tearoff" );
1119 // append the title as the very first entry if we have it
1122 Append(-2, m_title
);
1129 WX_CLEAR_LIST(wxMenuItemList
, m_items
);
1131 if ( GTK_IS_WIDGET( m_menu
))
1132 gtk_widget_destroy( m_menu
);
1134 gtk_object_unref( GTK_OBJECT(m_factory
) );
1137 bool wxMenu::GtkAppend(wxMenuItem
*mitem
)
1139 GtkWidget
*menuItem
;
1141 #if defined(USE_MENU_BITMAPS) || !GTK_CHECK_VERSION(1, 2, 0)
1142 bool appended
= FALSE
;
1145 // does this item terminate the current radio group?
1146 bool endOfRadioGroup
= TRUE
;
1148 if ( mitem
->IsSeparator() )
1150 GtkItemFactoryEntry entry
;
1151 entry
.path
= (char *)"/sep";
1152 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
1153 entry
.callback_action
= 0;
1154 entry
.item_type
= (char *)"<Separator>";
1155 entry
.accelerator
= (gchar
*) NULL
;
1157 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
1159 // this will be wrong for more than one separator. do we care?
1160 menuItem
= gtk_item_factory_get_widget( m_factory
, "<main>/sep" );
1162 // we might have a separator inside a radio group
1163 endOfRadioGroup
= FALSE
;
1165 else if ( mitem
->IsSubMenu() )
1167 // text has "_" instead of "&" after mitem->SetText()
1168 wxString
text( mitem
->GetText() );
1170 // local buffer in multibyte form
1173 strcat( buf
, wxGTK_CONV( text
) );
1175 GtkItemFactoryEntry entry
;
1177 entry
.callback
= (GtkItemFactoryCallback
) 0;
1178 entry
.callback_action
= 0;
1179 entry
.item_type
= (char *)"<Branch>";
1180 entry
.accelerator
= (gchar
*) NULL
;
1182 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
1184 wxString
path( mitem
->GetFactoryPath() );
1185 menuItem
= gtk_item_factory_get_item( m_factory
, wxGTK_CONV( path
) );
1187 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menuItem
), mitem
->GetSubMenu()->m_menu
);
1189 // if adding a submenu to a menu already existing in the menu bar, we
1190 // must set invoking window to allow processing events from this
1192 if ( m_invokingWindow
)
1193 wxMenubarSetInvokingWindow(mitem
->GetSubMenu(), m_invokingWindow
);
1195 #ifdef USE_MENU_BITMAPS
1196 else if (mitem
->GetBitmap().Ok()) // An item with bitmap
1198 wxString
text( mitem
->GetText() );
1199 const wxBitmap
*bitmap
= &mitem
->GetBitmap();
1201 menuItem
= gtk_pixmap_menu_item_new ();
1202 GtkWidget
*label
= gtk_accel_label_new ( wxGTK_CONV( text
) );
1203 gtk_misc_set_alignment (GTK_MISC (label
), 0.0, 0.5);
1204 gtk_container_add (GTK_CONTAINER (menuItem
), label
);
1205 gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (label
), menuItem
);
1207 GdkModifierType accel_mods
;
1209 // accelerator for the item, as specified by its label
1210 // (ex. Ctrl+O for open)
1211 gtk_accelerator_parse(GetHotKey(*mitem
).c_str(),
1212 &accel_key
, &accel_mods
);
1213 if (accel_key
!= GDK_VoidSymbol
)
1215 gtk_widget_add_accelerator (menuItem
,
1217 gtk_menu_get_accel_group(
1219 accel_key
, accel_mods
,
1223 // accelerator for the underlined char (ex ALT+F for the File menu)
1224 accel_key
= gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV( text
) );
1225 if (accel_key
!= GDK_VoidSymbol
)
1227 gtk_widget_add_accelerator (menuItem
,
1229 gtk_menu_ensure_uline_accel_group (
1235 gtk_widget_show (label
);
1237 mitem
->SetLabelWidget(label
);
1239 GtkWidget
* pixmap
= gtk_pixmap_new( bitmap
->GetPixmap(), bitmap
->GetMask() ? bitmap
->GetMask()->GetBitmap() : (GdkBitmap
* )NULL
);
1240 gtk_widget_show(pixmap
);
1241 gtk_pixmap_menu_item_set_pixmap(GTK_PIXMAP_MENU_ITEM( menuItem
), pixmap
);
1243 gtk_signal_connect( GTK_OBJECT(menuItem
), "activate",
1244 GTK_SIGNAL_FUNC(gtk_menu_clicked_callback
),
1247 gtk_menu_append( GTK_MENU(m_menu
), menuItem
);
1249 gtk_widget_show( menuItem
);
1251 appended
= TRUE
; // We've done this, don't do it again
1253 #endif // USE_MENU_BITMAPS
1254 else // a normal item
1256 // text has "_" instead of "&" after mitem->SetText() so don't use it
1257 wxString
text( mitem
->GetText() );
1259 // buffers containing the menu item path and type in multibyte form
1263 strcpy( bufPath
, "/" );
1264 strncat( bufPath
, wxGTK_CONV(text
), WXSIZEOF(bufPath
) - 2 );
1265 bufPath
[WXSIZEOF(bufPath
) - 1] = '\0';
1267 GtkItemFactoryEntry entry
;
1268 entry
.path
= bufPath
;
1269 entry
.callback
= (GtkItemFactoryCallback
) gtk_menu_clicked_callback
;
1270 entry
.callback_action
= 0;
1273 const char *item_type
;
1274 switch ( mitem
->GetKind() )
1277 item_type
= "<CheckItem>";
1281 if ( m_pathLastRadio
.empty() )
1283 // start of a new radio group
1284 item_type
= "<RadioItem>";
1285 wxString
tmp( wxGTK_CONV_BACK( bufPath
) );
1287 m_pathLastRadio
= tmp
;
1289 else // continue the radio group
1291 pathRadio
= m_pathLastRadio
;
1292 pathRadio
.Replace(wxT("_"), wxT(""));
1293 pathRadio
.Prepend(wxT("<main>/"));
1295 strncpy(bufType
, wxGTK_CONV(pathRadio
), WXSIZEOF(bufType
));
1296 bufType
[WXSIZEOF(bufType
) - 1] = '\0';
1297 item_type
= bufType
;
1300 // continue the existing radio group, if any
1301 endOfRadioGroup
= FALSE
;
1305 wxFAIL_MSG( _T("unexpected menu item kind") );
1309 item_type
= "<Item>";
1313 entry
.item_type
= (char *)item_type
; // cast needed for GTK+
1314 entry
.accelerator
= (gchar
*) NULL
;
1317 // due to an apparent bug in GTK+, we have to use a static buffer here -
1318 // otherwise GTK+ 1.2.2 manages to override the memory we pass to it
1320 char s_accel
[50]; // should be big enough, we check for overruns
1321 wxString
tmp( GetHotKey(*mitem
) );
1322 strncpy(s_accel
, wxGTK_CONV( tmp
), WXSIZEOF(s_accel
));
1323 s_accel
[WXSIZEOF(s_accel
) - 1] = '\0';
1324 entry
.accelerator
= s_accel
;
1325 #else // !wxUSE_ACCEL
1326 entry
.accelerator
= (char*) NULL
;
1327 #endif // wxUSE_ACCEL/!wxUSE_ACCEL
1329 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); /* what is 2 ? */
1331 wxString
path( mitem
->GetFactoryPath() );
1332 menuItem
= gtk_item_factory_get_widget( m_factory
, wxGTK_CONV( path
) );
1335 wxLogError( wxT("Wrong menu path: %s\n"), path
.c_str() );
1338 if ( !mitem
->IsSeparator() )
1340 wxASSERT_MSG( menuItem
, wxT("invalid menuitem") );
1342 gtk_signal_connect( GTK_OBJECT(menuItem
), "select",
1343 GTK_SIGNAL_FUNC(gtk_menu_hilight_callback
),
1346 gtk_signal_connect( GTK_OBJECT(menuItem
), "deselect",
1347 GTK_SIGNAL_FUNC(gtk_menu_nolight_callback
),
1351 mitem
->SetMenuItem(menuItem
);
1353 if ( endOfRadioGroup
)
1355 m_pathLastRadio
.clear();
1361 wxMenuItem
* wxMenu::DoAppend(wxMenuItem
*mitem
)
1363 if (!GtkAppend(mitem
))
1365 return wxMenuBase::DoAppend(mitem
);
1368 wxMenuItem
* wxMenu::DoInsert(size_t pos
, wxMenuItem
*item
)
1370 if ( !wxMenuBase::DoInsert(pos
, item
) )
1373 // GTK+ doesn't have a function to insert a menu using GtkItemFactory (as
1374 // of version 1.2.6), so we first append the item and then change its
1376 if ( !GtkAppend(item
) )
1379 if ( m_style
& wxMENU_TEAROFF
)
1381 // change the position as the first item is the tear-off marker
1385 GtkMenuShell
*menu_shell
= GTK_MENU_SHELL(m_factory
->widget
);
1386 gpointer data
= g_list_last(menu_shell
->children
)->data
;
1387 menu_shell
->children
= g_list_remove(menu_shell
->children
, data
);
1388 menu_shell
->children
= g_list_insert(menu_shell
->children
, data
, pos
);
1393 wxMenuItem
*wxMenu::DoRemove(wxMenuItem
*item
)
1395 if ( !wxMenuBase::DoRemove(item
) )
1396 return (wxMenuItem
*)NULL
;
1398 // TODO: this code doesn't delete the item factory item and this seems
1399 // impossible as of GTK 1.2.6.
1400 gtk_widget_destroy( item
->GetMenuItem() );
1405 int wxMenu::FindMenuIdByMenuItem( GtkWidget
*menuItem
) const
1407 wxMenuItemList::compatibility_iterator node
= m_items
.GetFirst();
1410 wxMenuItem
*item
= node
->GetData();
1411 if (item
->GetMenuItem() == menuItem
)
1412 return item
->GetId();
1413 node
= node
->GetNext();
1419 // ----------------------------------------------------------------------------
1421 // ----------------------------------------------------------------------------
1423 #if GTK_CHECK_VERSION(1, 2, 0) && wxUSE_ACCEL
1425 static wxString
GetHotKey( const wxMenuItem
& item
)
1429 wxAcceleratorEntry
*accel
= item
.GetAccel();
1432 int flags
= accel
->GetFlags();
1433 if ( flags
& wxACCEL_ALT
)
1434 hotkey
+= wxT("<alt>");
1435 if ( flags
& wxACCEL_CTRL
)
1436 hotkey
+= wxT("<control>");
1437 if ( flags
& wxACCEL_SHIFT
)
1438 hotkey
+= wxT("<shift>");
1440 int code
= accel
->GetKeyCode();
1455 hotkey
<< wxT('F') << code
- WXK_F1
+ 1;
1458 // TODO: we should use gdk_keyval_name() (a.k.a.
1459 // XKeysymToString) here as well as hardcoding the keysym
1460 // names this might be not portable
1461 case WXK_NUMPAD_INSERT
:
1462 hotkey
<< wxT("KP_Insert" );
1464 case WXK_NUMPAD_DELETE
:
1465 hotkey
<< wxT("KP_Delete" );
1468 hotkey
<< wxT("Insert" );
1471 hotkey
<< wxT("Delete" );
1474 hotkey
<< wxT("Up" );
1477 hotkey
<< wxT("Down" );
1481 hotkey
<< wxT("Prior" );
1485 hotkey
<< wxT("Next" );
1488 hotkey
<< wxT("Left" );
1491 hotkey
<< wxT("Right" );
1494 hotkey
<< wxT("Home" );
1497 hotkey
<< wxT("End" );
1500 hotkey
<< wxT("Return" );
1503 // if there are any other keys wxGetAccelFromString() may
1504 // return, we should process them here
1509 wxString name
= wxGTK_CONV_BACK( gdk_keyval_name((guint
)code
) );
1517 wxFAIL_MSG( wxT("unknown keyboard accel") );
1526 #endif // wxUSE_ACCEL
1529 //-----------------------------------------------------------------------------
1530 // substitute for missing GtkPixmapMenuItem
1531 //-----------------------------------------------------------------------------
1533 #ifdef USE_MENU_BITMAPS
1536 * Copyright (C) 1998, 1999, 2000 Free Software Foundation
1537 * All rights reserved.
1539 * This file is part of the Gnome Library.
1541 * The Gnome Library is free software; you can redistribute it and/or
1542 * modify it under the terms of the GNU Library General Public License as
1543 * published by the Free Software Foundation; either version 2 of the
1544 * License, or (at your option) any later version.
1546 * The Gnome Library is distributed in the hope that it will be useful,
1547 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1548 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1549 * Library General Public License for more details.
1551 * You should have received a copy of the GNU Library General Public
1552 * License along with the Gnome Library; see the file COPYING.LIB. If not,
1553 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
1554 * Boston, MA 02111-1307, USA.
1560 /* Author: Dietmar Maurer <dm@vlsivie.tuwien.ac.at> */
1562 #include <gtk/gtkaccellabel.h>
1563 #include <gtk/gtksignal.h>
1564 #include <gtk/gtkmenuitem.h>
1565 #include <gtk/gtkmenu.h>
1566 #include <gtk/gtkcontainer.h>
1571 static void gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass
*klass
);
1572 static void gtk_pixmap_menu_item_init (GtkPixmapMenuItem
*menu_item
);
1573 static void gtk_pixmap_menu_item_draw (GtkWidget
*widget
,
1574 GdkRectangle
*area
);
1575 static gint
gtk_pixmap_menu_item_expose (GtkWidget
*widget
,
1576 GdkEventExpose
*event
);
1578 /* we must override the following functions */
1580 static void gtk_pixmap_menu_item_map (GtkWidget
*widget
);
1581 static void gtk_pixmap_menu_item_size_allocate (GtkWidget
*widget
,
1582 GtkAllocation
*allocation
);
1583 static void gtk_pixmap_menu_item_forall (GtkContainer
*container
,
1584 gboolean include_internals
,
1585 GtkCallback callback
,
1586 gpointer callback_data
);
1587 static void gtk_pixmap_menu_item_size_request (GtkWidget
*widget
,
1588 GtkRequisition
*requisition
);
1589 static void gtk_pixmap_menu_item_remove (GtkContainer
*container
,
1592 static void changed_have_pixmap_status (GtkPixmapMenuItem
*menu_item
);
1594 static GtkMenuItemClass
*parent_class
= NULL
;
1598 #define BORDER_SPACING 3
1599 #define PMAP_WIDTH 20
1602 gtk_pixmap_menu_item_get_type (void)
1604 static GtkType pixmap_menu_item_type
= 0;
1606 if (!pixmap_menu_item_type
)
1608 GtkTypeInfo pixmap_menu_item_info
=
1610 (char *)"GtkPixmapMenuItem",
1611 sizeof (GtkPixmapMenuItem
),
1612 sizeof (GtkPixmapMenuItemClass
),
1613 (GtkClassInitFunc
) gtk_pixmap_menu_item_class_init
,
1614 (GtkObjectInitFunc
) gtk_pixmap_menu_item_init
,
1615 /* reserved_1 */ NULL
,
1616 /* reserved_2 */ NULL
,
1617 (GtkClassInitFunc
) NULL
,
1620 pixmap_menu_item_type
= gtk_type_unique (gtk_menu_item_get_type (),
1621 &pixmap_menu_item_info
);
1624 return pixmap_menu_item_type
;
1628 * gtk_pixmap_menu_item_new
1630 * Creates a new pixmap menu item. Use gtk_pixmap_menu_item_set_pixmap()
1631 * to set the pixmap wich is displayed at the left side.
1634 * &GtkWidget pointer to new menu item
1638 gtk_pixmap_menu_item_new (void)
1640 return GTK_WIDGET (gtk_type_new (gtk_pixmap_menu_item_get_type ()));
1644 gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass
*klass
)
1646 GtkObjectClass
*object_class
;
1647 GtkWidgetClass
*widget_class
;
1648 GtkMenuItemClass
*menu_item_class
;
1649 GtkContainerClass
*container_class
;
1651 object_class
= (GtkObjectClass
*) klass
;
1652 widget_class
= (GtkWidgetClass
*) klass
;
1653 menu_item_class
= (GtkMenuItemClass
*) klass
;
1654 container_class
= (GtkContainerClass
*) klass
;
1656 parent_class
= (GtkMenuItemClass
*) gtk_type_class (gtk_menu_item_get_type ());
1658 widget_class
->draw
= gtk_pixmap_menu_item_draw
;
1659 widget_class
->expose_event
= gtk_pixmap_menu_item_expose
;
1660 widget_class
->map
= gtk_pixmap_menu_item_map
;
1661 widget_class
->size_allocate
= gtk_pixmap_menu_item_size_allocate
;
1662 widget_class
->size_request
= gtk_pixmap_menu_item_size_request
;
1664 container_class
->forall
= gtk_pixmap_menu_item_forall
;
1665 container_class
->remove
= gtk_pixmap_menu_item_remove
;
1667 klass
->orig_toggle_size
= menu_item_class
->toggle_size
;
1668 klass
->have_pixmap_count
= 0;
1672 gtk_pixmap_menu_item_init (GtkPixmapMenuItem
*menu_item
)
1676 mi
= GTK_MENU_ITEM (menu_item
);
1678 menu_item
->pixmap
= NULL
;
1682 gtk_pixmap_menu_item_draw (GtkWidget
*widget
,
1685 g_return_if_fail (widget
!= NULL
);
1686 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
));
1687 g_return_if_fail (area
!= NULL
);
1689 if (GTK_WIDGET_CLASS (parent_class
)->draw
)
1690 (* GTK_WIDGET_CLASS (parent_class
)->draw
) (widget
, area
);
1692 if (GTK_WIDGET_DRAWABLE (widget
) &&
1693 GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
) {
1694 gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
),NULL
);
1699 gtk_pixmap_menu_item_expose (GtkWidget
*widget
,
1700 GdkEventExpose
*event
)
1702 g_return_val_if_fail (widget
!= NULL
, FALSE
);
1703 g_return_val_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
), FALSE
);
1704 g_return_val_if_fail (event
!= NULL
, FALSE
);
1706 if (GTK_WIDGET_CLASS (parent_class
)->expose_event
)
1707 (* GTK_WIDGET_CLASS (parent_class
)->expose_event
) (widget
, event
);
1709 if (GTK_WIDGET_DRAWABLE (widget
) &&
1710 GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
) {
1711 gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
),NULL
);
1718 * gtk_pixmap_menu_item_set_pixmap
1719 * @menu_item: Pointer to the pixmap menu item
1720 * @pixmap: Pointer to a pixmap widget
1722 * Set the pixmap of the menu item.
1727 gtk_pixmap_menu_item_set_pixmap (GtkPixmapMenuItem
*menu_item
,
1730 g_return_if_fail (menu_item
!= NULL
);
1731 g_return_if_fail (pixmap
!= NULL
);
1732 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (menu_item
));
1733 g_return_if_fail (GTK_IS_WIDGET (pixmap
));
1734 g_return_if_fail (menu_item
->pixmap
== NULL
);
1736 gtk_widget_set_parent (pixmap
, GTK_WIDGET (menu_item
));
1737 menu_item
->pixmap
= pixmap
;
1739 if (GTK_WIDGET_REALIZED (pixmap
->parent
) &&
1740 !GTK_WIDGET_REALIZED (pixmap
))
1741 gtk_widget_realize (pixmap
);
1743 if (GTK_WIDGET_VISIBLE (pixmap
->parent
)) {
1744 if (GTK_WIDGET_MAPPED (pixmap
->parent
) &&
1745 GTK_WIDGET_VISIBLE(pixmap
) &&
1746 !GTK_WIDGET_MAPPED (pixmap
))
1747 gtk_widget_map (pixmap
);
1750 changed_have_pixmap_status(menu_item
);
1752 if (GTK_WIDGET_VISIBLE (pixmap
) && GTK_WIDGET_VISIBLE (menu_item
))
1753 gtk_widget_queue_resize (pixmap
);
1757 gtk_pixmap_menu_item_map (GtkWidget
*widget
)
1759 GtkPixmapMenuItem
*menu_item
;
1761 g_return_if_fail (widget
!= NULL
);
1762 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
));
1764 menu_item
= GTK_PIXMAP_MENU_ITEM(widget
);
1766 GTK_WIDGET_CLASS(parent_class
)->map(widget
);
1768 if (menu_item
->pixmap
&&
1769 GTK_WIDGET_VISIBLE (menu_item
->pixmap
) &&
1770 !GTK_WIDGET_MAPPED (menu_item
->pixmap
))
1771 gtk_widget_map (menu_item
->pixmap
);
1775 gtk_pixmap_menu_item_size_allocate (GtkWidget
*widget
,
1776 GtkAllocation
*allocation
)
1778 GtkPixmapMenuItem
*pmenu_item
;
1780 pmenu_item
= GTK_PIXMAP_MENU_ITEM(widget
);
1782 if (pmenu_item
->pixmap
&& GTK_WIDGET_VISIBLE(pmenu_item
))
1784 GtkAllocation child_allocation
;
1787 border_width
= GTK_CONTAINER (widget
)->border_width
;
1789 child_allocation
.width
= pmenu_item
->pixmap
->requisition
.width
;
1790 child_allocation
.height
= pmenu_item
->pixmap
->requisition
.height
;
1791 child_allocation
.x
= border_width
+ BORDER_SPACING
;
1792 child_allocation
.y
= (border_width
+ BORDER_SPACING
1793 + (((allocation
->height
- child_allocation
.height
) - child_allocation
.x
)
1794 / 2)); /* center pixmaps vertically */
1795 gtk_widget_size_allocate (pmenu_item
->pixmap
, &child_allocation
);
1798 if (GTK_WIDGET_CLASS (parent_class
)->size_allocate
)
1799 GTK_WIDGET_CLASS(parent_class
)->size_allocate (widget
, allocation
);
1803 gtk_pixmap_menu_item_forall (GtkContainer
*container
,
1804 gboolean include_internals
,
1805 GtkCallback callback
,
1806 gpointer callback_data
)
1808 GtkPixmapMenuItem
*menu_item
;
1810 g_return_if_fail (container
!= NULL
);
1811 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container
));
1812 g_return_if_fail (callback
!= NULL
);
1814 menu_item
= GTK_PIXMAP_MENU_ITEM (container
);
1816 if (menu_item
->pixmap
)
1817 (* callback
) (menu_item
->pixmap
, callback_data
);
1819 GTK_CONTAINER_CLASS(parent_class
)->forall(container
,include_internals
,
1820 callback
,callback_data
);
1824 gtk_pixmap_menu_item_size_request (GtkWidget
*widget
,
1825 GtkRequisition
*requisition
)
1827 GtkPixmapMenuItem
*menu_item
;
1828 GtkRequisition req
= {0, 0};
1830 g_return_if_fail (widget
!= NULL
);
1831 g_return_if_fail (GTK_IS_MENU_ITEM (widget
));
1832 g_return_if_fail (requisition
!= NULL
);
1834 GTK_WIDGET_CLASS(parent_class
)->size_request(widget
,requisition
);
1836 menu_item
= GTK_PIXMAP_MENU_ITEM (widget
);
1838 if (menu_item
->pixmap
)
1839 gtk_widget_size_request(menu_item
->pixmap
, &req
);
1841 requisition
->height
= MAX(req
.height
+ GTK_CONTAINER(widget
)->border_width
+ BORDER_SPACING
, (unsigned int) requisition
->height
);
1842 requisition
->width
+= (req
.width
+ GTK_CONTAINER(widget
)->border_width
+ BORDER_SPACING
);
1846 gtk_pixmap_menu_item_remove (GtkContainer
*container
,
1850 gboolean widget_was_visible
;
1852 g_return_if_fail (container
!= NULL
);
1853 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container
));
1854 g_return_if_fail (child
!= NULL
);
1855 g_return_if_fail (GTK_IS_WIDGET (child
));
1857 bin
= GTK_BIN (container
);
1858 g_return_if_fail ((bin
->child
== child
||
1859 (GTK_PIXMAP_MENU_ITEM(container
)->pixmap
== child
)));
1861 widget_was_visible
= GTK_WIDGET_VISIBLE (child
);
1863 gtk_widget_unparent (child
);
1864 if (bin
->child
== child
)
1867 GTK_PIXMAP_MENU_ITEM(container
)->pixmap
= NULL
;
1868 changed_have_pixmap_status(GTK_PIXMAP_MENU_ITEM(container
));
1871 if (widget_was_visible
)
1872 gtk_widget_queue_resize (GTK_WIDGET (container
));
1876 /* important to only call this if there was actually a _change_ in pixmap == NULL */
1878 changed_have_pixmap_status (GtkPixmapMenuItem
*menu_item
)
1880 if (menu_item
->pixmap
!= NULL
) {
1881 GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
+= 1;
1883 if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
== 1) {
1884 /* Install pixmap toggle size */
1885 GTK_MENU_ITEM_GET_CLASS(menu_item
)->toggle_size
= MAX(GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->orig_toggle_size
, PMAP_WIDTH
);
1888 GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
-= 1;
1890 if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
== 0) {
1891 /* Install normal toggle size */
1892 GTK_MENU_ITEM_GET_CLASS(menu_item
)->toggle_size
= GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->orig_toggle_size
;
1896 /* Note that we actually need to do this for _all_ GtkPixmapMenuItem
1897 whenever the klass->toggle_size changes; but by doing it anytime
1898 this function is called, we get the same effect, just because of
1899 how the preferences option to show pixmaps works. Bogus, broken.
1901 if (GTK_WIDGET_VISIBLE(GTK_WIDGET(menu_item
)))
1902 gtk_widget_queue_resize(GTK_WIDGET(menu_item
));
1905 #endif // USE_MENU_BITMAPS