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, menu
);
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 /* GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has. */
204 #if GTK_CHECK_VERSION(1, 2, 1)
205 m_accel
= gtk_accel_group_new();
206 m_factory
= gtk_item_factory_new( GTK_TYPE_MENU_BAR
, "<main>", m_accel
);
207 m_menubar
= gtk_item_factory_get_widget( m_factory
, "<main>" );
209 m_menubar
= gtk_menu_bar_new();
212 if (style
& wxMB_DOCKABLE
)
214 m_widget
= gtk_handle_box_new();
215 gtk_container_add( GTK_CONTAINER(m_widget
), GTK_WIDGET(m_menubar
) );
216 gtk_widget_show( GTK_WIDGET(m_menubar
) );
220 m_widget
= GTK_WIDGET(m_menubar
);
228 wxMenuBar::wxMenuBar()
230 /* the parent window is known after wxFrame::SetMenu() */
231 m_needParent
= FALSE
;
233 m_invokingWindow
= (wxWindow
*) NULL
;
235 if (!PreCreation( (wxWindow
*) NULL
, wxDefaultPosition
, wxDefaultSize
) ||
236 !CreateBase( (wxWindow
*) NULL
, -1, wxDefaultPosition
, wxDefaultSize
, 0, wxDefaultValidator
, wxT("menubar") ))
238 wxFAIL_MSG( wxT("wxMenuBar creation failed") );
242 /* GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has. */
243 #if GTK_CHECK_VERSION(1, 2, 1)
244 m_accel
= gtk_accel_group_new();
245 m_factory
= gtk_item_factory_new( GTK_TYPE_MENU_BAR
, "<main>", m_accel
);
246 m_menubar
= gtk_item_factory_get_widget( m_factory
, "<main>" );
248 m_menubar
= gtk_menu_bar_new();
251 m_widget
= GTK_WIDGET(m_menubar
);
258 wxMenuBar::~wxMenuBar()
260 // gtk_object_unref( GTK_OBJECT(m_factory) ); why not ?
263 static void wxMenubarUnsetInvokingWindow( wxMenu
*menu
, wxWindow
*win
)
265 menu
->SetInvokingWindow( (wxWindow
*) NULL
);
267 wxWindow
*top_frame
= win
;
268 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
269 top_frame
= top_frame
->GetParent();
271 /* support for native hot keys */
272 gtk_accel_group_detach( menu
->m_accel
, ACCEL_OBJ_CAST(top_frame
->m_widget
) );
274 wxMenuItemList::compatibility_iterator node
= menu
->GetMenuItems().GetFirst();
277 wxMenuItem
*menuitem
= node
->GetData();
278 if (menuitem
->IsSubMenu())
279 wxMenubarUnsetInvokingWindow( menuitem
->GetSubMenu(), win
);
280 node
= node
->GetNext();
284 static void wxMenubarSetInvokingWindow( wxMenu
*menu
, wxWindow
*win
)
286 menu
->SetInvokingWindow( win
);
288 #if GTK_CHECK_VERSION(1, 2, 1)
289 wxWindow
*top_frame
= win
;
290 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
291 top_frame
= top_frame
->GetParent();
293 /* support for native hot keys */
294 ACCEL_OBJECT
*obj
= ACCEL_OBJ_CAST(top_frame
->m_widget
);
295 if ( !g_slist_find( ACCEL_OBJECTS(menu
->m_accel
), obj
) )
296 gtk_accel_group_attach( menu
->m_accel
, obj
);
297 #endif // GTK+ 1.2.1+
299 wxMenuItemList::compatibility_iterator node
= menu
->GetMenuItems().GetFirst();
302 wxMenuItem
*menuitem
= node
->GetData();
303 if (menuitem
->IsSubMenu())
304 wxMenubarSetInvokingWindow( menuitem
->GetSubMenu(), win
);
305 node
= node
->GetNext();
309 void wxMenuBar::SetInvokingWindow( wxWindow
*win
)
311 m_invokingWindow
= win
;
312 #if GTK_CHECK_VERSION(1, 2, 1)
313 wxWindow
*top_frame
= win
;
314 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
315 top_frame
= top_frame
->GetParent();
317 /* support for native key accelerators indicated by underscroes */
318 ACCEL_OBJECT
*obj
= ACCEL_OBJ_CAST(top_frame
->m_widget
);
319 if ( !g_slist_find( ACCEL_OBJECTS(m_accel
), obj
) )
320 gtk_accel_group_attach( m_accel
, obj
);
321 #endif // GTK+ 1.2.1+
323 wxMenuList::compatibility_iterator node
= m_menus
.GetFirst();
326 wxMenu
*menu
= node
->GetData();
327 wxMenubarSetInvokingWindow( menu
, win
);
328 node
= node
->GetNext();
332 void wxMenuBar::UnsetInvokingWindow( wxWindow
*win
)
334 m_invokingWindow
= (wxWindow
*) NULL
;
335 #if GTK_CHECK_VERSION(1, 2, 1)
336 wxWindow
*top_frame
= win
;
337 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
338 top_frame
= top_frame
->GetParent();
340 // support for native key accelerators indicated by underscroes
341 gtk_accel_group_detach( m_accel
, ACCEL_OBJ_CAST(top_frame
->m_widget
) );
342 #endif // GTK+ 1.2.1+
344 wxMenuList::compatibility_iterator node
= m_menus
.GetFirst();
347 wxMenu
*menu
= node
->GetData();
348 wxMenubarUnsetInvokingWindow( menu
, win
);
349 node
= node
->GetNext();
353 bool wxMenuBar::Append( wxMenu
*menu
, const wxString
&title
)
355 if ( !wxMenuBarBase::Append( menu
, title
) )
358 return GtkAppend(menu
, title
);
361 bool wxMenuBar::GtkAppend(wxMenu
*menu
, const wxString
& title
)
363 wxString
str( wxReplaceUnderscore( title
) );
365 // This doesn't have much effect right now.
366 menu
->SetTitle( str
);
368 // GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has.
369 #if GTK_CHECK_VERSION(1, 2, 1)
372 buf
<< wxT('/') << str
.c_str();
374 // local buffer in multibyte form
376 strcpy(cbuf
, wxGTK_CONV(buf
) );
378 GtkItemFactoryEntry entry
;
379 entry
.path
= (gchar
*)cbuf
; // const_cast
380 entry
.accelerator
= (gchar
*) NULL
;
381 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
382 entry
.callback_action
= 0;
383 entry
.item_type
= (char *)"<Branch>";
385 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
386 // in order to get the pointer to the item we need the item text _without_ underscores
387 wxString tmp
= wxT("<main>/");
389 for ( pc
= str
; *pc
!= wxT('\0'); pc
++ )
391 // contrary to the common sense, we must throw out _all_ underscores,
392 // (i.e. "Hello__World" => "HelloWorld" and not "Hello_World" as we
393 // might naively think). IMHO it's a bug in GTK+ (VZ)
394 while (*pc
== wxT('_'))
398 menu
->m_owner
= gtk_item_factory_get_item( m_factory
, wxGTK_CONV( tmp
) );
399 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu
->m_owner
), menu
->m_menu
);
402 menu
->m_owner
= gtk_menu_item_new_with_label( wxGTK_CONV( str
) );
403 gtk_widget_show( menu
->m_owner
);
404 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu
->m_owner
), menu
->m_menu
);
406 gtk_menu_bar_append( GTK_MENU_BAR(m_menubar
), menu
->m_owner
);
410 gtk_signal_connect( GTK_OBJECT(menu
->m_owner
), "activate",
411 GTK_SIGNAL_FUNC(gtk_menu_open_callback
),
414 // m_invokingWindow is set after wxFrame::SetMenuBar(). This call enables
415 // addings menu later on.
416 if (m_invokingWindow
)
418 wxMenubarSetInvokingWindow( menu
, m_invokingWindow
);
420 // OPTIMISE ME: we should probably cache this, or pass it
421 // directly, but for now this is a minimal
422 // change to validate the new dynamic sizing.
423 // see (and refactor :) similar code in Remove
426 wxFrame
*frame
= wxDynamicCast( m_invokingWindow
, wxFrame
);
429 frame
->UpdateMenuBarSize();
435 bool wxMenuBar::Insert(size_t pos
, wxMenu
*menu
, const wxString
& title
)
437 if ( !wxMenuBarBase::Insert(pos
, menu
, title
) )
440 // GTK+ doesn't have a function to insert a menu using GtkItemFactory (as
441 // of version 1.2.6), so we first append the item and then change its
443 if ( !GtkAppend(menu
, title
) )
446 if (pos
+1 >= m_menus
.GetCount())
449 GtkMenuShell
*menu_shell
= GTK_MENU_SHELL(m_factory
->widget
);
450 gpointer data
= g_list_last(menu_shell
->children
)->data
;
451 menu_shell
->children
= g_list_remove(menu_shell
->children
, data
);
452 menu_shell
->children
= g_list_insert(menu_shell
->children
, data
, pos
);
457 wxMenu
*wxMenuBar::Replace(size_t pos
, wxMenu
*menu
, const wxString
& title
)
459 // remove the old item and insert a new one
460 wxMenu
*menuOld
= Remove(pos
);
461 if ( menuOld
&& !Insert(pos
, menu
, title
) )
463 return (wxMenu
*) NULL
;
466 // either Insert() succeeded or Remove() failed and menuOld is NULL
470 static wxMenu
*CopyMenu (wxMenu
*menu
)
472 wxMenu
*menucopy
= new wxMenu ();
473 wxMenuItemList::compatibility_iterator node
= menu
->GetMenuItems().GetFirst();
476 wxMenuItem
*item
= node
->GetData();
477 int itemid
= item
->GetId();
478 wxString text
= item
->GetText();
479 text
.Replace(wxT("_"), wxT("&"));
480 wxMenu
*submenu
= item
->GetSubMenu();
483 wxMenuItem
* itemcopy
= new wxMenuItem(menucopy
,
485 menu
->GetHelpString(itemid
));
486 itemcopy
->SetBitmap(item
->GetBitmap());
487 itemcopy
->SetCheckable(item
->IsCheckable());
488 menucopy
->Append(itemcopy
);
491 menucopy
->Append (itemid
, text
, CopyMenu(submenu
),
492 menu
->GetHelpString(itemid
));
494 node
= node
->GetNext();
500 wxMenu
*wxMenuBar::Remove(size_t pos
)
502 wxMenu
*menu
= wxMenuBarBase::Remove(pos
);
504 return (wxMenu
*) NULL
;
507 GtkMenuShell *menu_shell = GTK_MENU_SHELL(m_factory->widget);
509 printf( "factory entries before %d\n", (int)g_slist_length(m_factory->items) );
510 printf( "menu shell entries before %d\n", (int)g_list_length( menu_shell->children ) );
513 wxMenu
*menucopy
= CopyMenu( menu
);
515 // unparent calls unref() and that would delete the widget so we raise
516 // the ref count to 2 artificially before invoking unparent.
517 gtk_widget_ref( menu
->m_menu
);
518 gtk_widget_unparent( menu
->m_menu
);
520 gtk_widget_destroy( menu
->m_owner
);
525 printf( "factory entries after %d\n", (int)g_slist_length(m_factory->items) );
526 printf( "menu shell entries after %d\n", (int)g_list_length( menu_shell->children ) );
529 if (m_invokingWindow
)
531 // OPTIMISE ME: see comment in GtkAppend
533 wxFrame
*frame
= wxDynamicCast( m_invokingWindow
, wxFrame
);
536 frame
->UpdateMenuBarSize();
542 static int FindMenuItemRecursive( const wxMenu
*menu
, const wxString
&menuString
, const wxString
&itemString
)
544 if (wxMenuItem::GetLabelFromText(menu
->GetTitle()) == wxMenuItem::GetLabelFromText(menuString
))
546 int res
= menu
->FindItem( itemString
);
547 if (res
!= wxNOT_FOUND
)
551 wxMenuItemList::compatibility_iterator node
= menu
->GetMenuItems().GetFirst();
554 wxMenuItem
*item
= node
->GetData();
555 if (item
->IsSubMenu())
556 return FindMenuItemRecursive(item
->GetSubMenu(), menuString
, itemString
);
558 node
= node
->GetNext();
564 int wxMenuBar::FindMenuItem( const wxString
&menuString
, const wxString
&itemString
) const
566 wxMenuList::compatibility_iterator node
= m_menus
.GetFirst();
569 wxMenu
*menu
= node
->GetData();
570 int res
= FindMenuItemRecursive( menu
, menuString
, itemString
);
573 node
= node
->GetNext();
579 // Find a wxMenuItem using its id. Recurses down into sub-menus
580 static wxMenuItem
* FindMenuItemByIdRecursive(const wxMenu
* menu
, int id
)
582 wxMenuItem
* result
= menu
->FindChildItem(id
);
584 wxMenuItemList::compatibility_iterator node
= menu
->GetMenuItems().GetFirst();
585 while ( node
&& result
== NULL
)
587 wxMenuItem
*item
= node
->GetData();
588 if (item
->IsSubMenu())
590 result
= FindMenuItemByIdRecursive( item
->GetSubMenu(), id
);
592 node
= node
->GetNext();
598 wxMenuItem
* wxMenuBar::FindItem( int id
, wxMenu
**menuForItem
) const
600 wxMenuItem
* result
= 0;
601 wxMenuList::compatibility_iterator node
= m_menus
.GetFirst();
602 while (node
&& result
== 0)
604 wxMenu
*menu
= node
->GetData();
605 result
= FindMenuItemByIdRecursive( menu
, id
);
606 node
= node
->GetNext();
611 *menuForItem
= result
? result
->GetMenu() : (wxMenu
*)NULL
;
617 void wxMenuBar::EnableTop( size_t pos
, bool flag
)
619 wxMenuList::compatibility_iterator node
= m_menus
.Item( pos
);
621 wxCHECK_RET( node
, wxT("menu not found") );
623 wxMenu
* menu
= node
->GetData();
626 gtk_widget_set_sensitive( menu
->m_owner
, flag
);
629 wxString
wxMenuBar::GetLabelTop( size_t pos
) const
631 wxMenuList::compatibility_iterator node
= m_menus
.Item( pos
);
633 wxCHECK_MSG( node
, wxT("invalid"), wxT("menu not found") );
635 wxMenu
* menu
= node
->GetData();
638 wxString
text( menu
->GetTitle() );
639 for ( const wxChar
*pc
= text
.c_str(); *pc
; pc
++ )
641 if ( *pc
== wxT('_') )
643 // '_' is the escape character for GTK+
647 // don't remove ampersands '&' since if we have them in the menu title
648 // it means that they were doubled to indicate "&" instead of accelerator
656 void wxMenuBar::SetLabelTop( size_t pos
, const wxString
& label
)
658 wxMenuList::compatibility_iterator node
= m_menus
.Item( pos
);
660 wxCHECK_RET( node
, wxT("menu not found") );
662 wxMenu
* menu
= node
->GetData();
664 wxString
str( wxReplaceUnderscore( label
) );
666 menu
->SetTitle( str
);
670 GtkLabel
*label
= GTK_LABEL( GTK_BIN(menu
->m_owner
)->child
);
673 gtk_label_set( label
, wxGTK_CONV( str
) );
675 /* reparse key accel */
676 (void)gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV( str
) );
677 gtk_accel_label_refetch( GTK_ACCEL_LABEL(label
) );
682 //-----------------------------------------------------------------------------
684 //-----------------------------------------------------------------------------
686 static void gtk_menu_clicked_callback( GtkWidget
*widget
, wxMenu
*menu
)
689 wxapp_install_idle_handler();
691 int id
= menu
->FindMenuIdByMenuItem(widget
);
693 /* should find it for normal (not popup) menu */
694 wxASSERT_MSG( (id
!= -1) || (menu
->GetInvokingWindow() != NULL
),
695 _T("menu item not found in gtk_menu_clicked_callback") );
697 if (!menu
->IsEnabled(id
))
700 wxMenuItem
* item
= menu
->FindChildItem( id
);
701 wxCHECK_RET( item
, wxT("error in menu item callback") );
703 if (item
->IsCheckable())
705 bool isReallyChecked
= item
->IsChecked(),
706 isInternallyChecked
= item
->wxMenuItemBase::IsChecked();
708 // ensure that the internal state is always consistent with what is
709 // shown on the screen
710 item
->wxMenuItemBase::Check(isReallyChecked
);
712 // we must not report the events for the radio button going up nor the
713 // events resulting from the calls to wxMenuItem::Check()
714 if ( (item
->GetKind() == wxITEM_RADIO
&& !isReallyChecked
) ||
715 (isInternallyChecked
== isReallyChecked
) )
722 // Is this menu on a menubar? (possibly nested)
723 wxFrame
* frame
= NULL
;
725 while ( pm
&& !frame
)
727 if ( pm
->IsAttached() )
728 frame
= pm
->GetMenuBar()->GetFrame();
729 pm
= pm
->GetParent();
734 // If it is attached then let the frame send the event.
735 // Don't call frame->ProcessCommand(id) because it toggles
736 // checkable items and we've already done that above.
737 wxCommandEvent
commandEvent(wxEVT_COMMAND_MENU_SELECTED
, id
);
738 commandEvent
.SetEventObject(frame
);
739 if (item
->IsCheckable())
740 commandEvent
.SetInt(item
->IsChecked());
742 frame
->GetEventHandler()->ProcessEvent(commandEvent
);
746 // otherwise let the menu have it
747 menu
->SendEvent(id
, item
->IsCheckable() ? item
->IsChecked() : -1);
751 //-----------------------------------------------------------------------------
753 //-----------------------------------------------------------------------------
755 static void gtk_menu_hilight_callback( GtkWidget
*widget
, wxMenu
*menu
)
757 if (g_isIdle
) wxapp_install_idle_handler();
759 int id
= menu
->FindMenuIdByMenuItem(widget
);
761 wxASSERT( id
!= -1 ); // should find it!
763 if (!menu
->IsEnabled(id
))
766 wxMenuEvent
event( wxEVT_MENU_HIGHLIGHT
, id
);
767 event
.SetEventObject( menu
);
769 wxEvtHandler
* handler
= menu
->GetEventHandler();
770 if (handler
&& handler
->ProcessEvent(event
))
773 wxWindow
*win
= menu
->GetInvokingWindow();
774 if (win
) win
->GetEventHandler()->ProcessEvent( event
);
777 //-----------------------------------------------------------------------------
779 //-----------------------------------------------------------------------------
781 static void gtk_menu_nolight_callback( GtkWidget
*widget
, wxMenu
*menu
)
783 if (g_isIdle
) wxapp_install_idle_handler();
785 int id
= menu
->FindMenuIdByMenuItem(widget
);
787 wxASSERT( id
!= -1 ); // should find it!
789 if (!menu
->IsEnabled(id
))
792 wxMenuEvent
event( wxEVT_MENU_HIGHLIGHT
, -1 );
793 event
.SetEventObject( menu
);
795 wxEvtHandler
* handler
= menu
->GetEventHandler();
796 if (handler
&& handler
->ProcessEvent(event
))
799 wxWindow
*win
= menu
->GetInvokingWindow();
801 win
->GetEventHandler()->ProcessEvent( event
);
804 //-----------------------------------------------------------------------------
806 //-----------------------------------------------------------------------------
808 IMPLEMENT_DYNAMIC_CLASS(wxMenuItem
, wxObject
)
810 wxMenuItem
*wxMenuItemBase::New(wxMenu
*parentMenu
,
812 const wxString
& name
,
813 const wxString
& help
,
817 return new wxMenuItem(parentMenu
, id
, name
, help
, kind
, subMenu
);
820 wxMenuItem::wxMenuItem(wxMenu
*parentMenu
,
822 const wxString
& text
,
823 const wxString
& help
,
826 : wxMenuItemBase(parentMenu
, id
, text
, help
, kind
, subMenu
)
831 wxMenuItem::wxMenuItem(wxMenu
*parentMenu
,
833 const wxString
& text
,
834 const wxString
& help
,
837 : wxMenuItemBase(parentMenu
, id
, text
, help
,
838 isCheckable
? wxITEM_CHECK
: wxITEM_NORMAL
, subMenu
)
843 void wxMenuItem::Init(const wxString
& text
)
845 m_labelWidget
= (GtkWidget
*) NULL
;
846 m_menuItem
= (GtkWidget
*) NULL
;
851 wxMenuItem::~wxMenuItem()
853 // don't delete menu items, the menus take care of that
856 // return the menu item text without any menu accels
858 wxString
wxMenuItemBase::GetLabelFromText(const wxString
& text
)
862 for ( const wxChar
*pc
= text
.c_str(); *pc
; pc
++ )
864 if ( *pc
== wxT('_') )
866 // GTK 1.2 escapes "xxx_xxx" to "xxx__xxx"
872 #if GTK_CHECK_VERSION(2, 0, 0)
873 if ( *pc
== wxT('\\') )
875 // GTK 2.0 escapes "xxx/xxx" to "xxx\/xxx"
882 if ( (*pc
== wxT('&')) && (*(pc
+1) != wxT('&')) )
885 // "&" is doubled to indicate "&" instead of accelerator
892 // wxPrintf( L"text %s label %s\n", text.c_str(), label.c_str() );
897 void wxMenuItem::SetText( const wxString
& str
)
899 // Some optimization to avoid flicker
900 wxString oldLabel
= m_text
;
901 oldLabel
= wxStripMenuCodes(oldLabel
.BeforeFirst('\t'));
902 oldLabel
.Replace(wxT("_"), wxT(""));
903 wxString label1
= wxStripMenuCodes(str
.BeforeFirst('\t'));
904 if (oldLabel
== label1
)
913 label
= (GtkLabel
*) m_labelWidget
;
915 label
= GTK_LABEL( GTK_BIN(m_menuItem
)->child
);
917 #if GTK_CHECK_VERSION(2, 0, 0)
918 // We have to imitate item_factory_unescape_label here
920 for (size_t n
= 0; n
< m_text
.Len(); n
++)
922 if (m_text
[n
] != wxT('\\'))
926 gtk_label_set_text_with_mnemonic( GTK_LABEL(label
), wxGTK_CONV(tmp
) );
929 gtk_label_set( label
, wxGTK_CONV( m_text
) );
932 (void)gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV(m_text
) );
933 gtk_accel_label_refetch( GTK_ACCEL_LABEL(label
) );
938 // it's valid for this function to be called even if m_menuItem == NULL
939 void wxMenuItem::DoSetText( const wxString
& str
)
941 // '\t' is the deliminator indicating a hot key
943 const wxChar
*pc
= str
;
944 while ( (*pc
!= wxT('\0')) && (*pc
!= wxT('\t')) )
946 if ((*pc
== wxT('&')) && (*(pc
+1) == wxT('&')))
948 // "&" is doubled to indicate "&" instead of accelerator
952 else if (*pc
== wxT('&'))
956 #if GTK_CHECK_VERSION(2, 0, 0)
957 else if ( *pc
== wxT('_') ) // escape underscores
959 // m_text << wxT("__"); doesn't work
962 else if (*pc
== wxT('/')) // we have to escape slashes
964 m_text
<< wxT("\\/");
966 else if (*pc
== wxT('\\')) // we have to double backslashes
968 m_text
<< wxT("\\\\");
971 else if ( *pc
== wxT('_') ) // escape underscores
975 else if (*pc
== wxT('/')) /* we have to filter out slashes ... */
977 m_text
<< wxT('\\'); /* ... and replace them with back slashes */
986 // wxPrintf( L"str %s m_text %s\n", str.c_str(), m_text.c_str() );
999 wxAcceleratorEntry
*wxMenuItem::GetAccel() const
1004 return (wxAcceleratorEntry
*)NULL
;
1007 // as wxGetAccelFromString() looks for TAB, insert a dummy one here
1009 label
<< wxT('\t') << GetHotKey();
1011 return wxGetAccelFromString(label
);
1014 #endif // wxUSE_ACCEL
1016 void wxMenuItem::Check( bool check
)
1018 wxCHECK_RET( m_menuItem
, wxT("invalid menu item") );
1020 if (check
== m_isChecked
)
1023 wxMenuItemBase::Check( check
);
1025 switch ( GetKind() )
1029 gtk_check_menu_item_set_state( (GtkCheckMenuItem
*)m_menuItem
, (gint
)check
);
1033 wxFAIL_MSG( _T("can't check this item") );
1037 void wxMenuItem::Enable( bool enable
)
1039 wxCHECK_RET( m_menuItem
, wxT("invalid menu item") );
1041 gtk_widget_set_sensitive( m_menuItem
, enable
);
1042 wxMenuItemBase::Enable( enable
);
1045 bool wxMenuItem::IsChecked() const
1047 wxCHECK_MSG( m_menuItem
, FALSE
, wxT("invalid menu item") );
1049 wxCHECK_MSG( IsCheckable(), FALSE
,
1050 wxT("can't get state of uncheckable item!") );
1052 return ((GtkCheckMenuItem
*)m_menuItem
)->active
!= 0;
1055 wxString
wxMenuItem::GetFactoryPath() const
1057 // In order to get the pointer to the item we need the item
1058 // text _without_ underscores in GTK 1.2
1059 wxString
path( wxT("<main>/") );
1061 for ( const wxChar
*pc
= m_text
.c_str(); *pc
; pc
++ )
1063 if ( *pc
== wxT('_') )
1068 // remove '_' unconditionally
1073 // don't remove ampersands '&' since if we have them in the menu item title
1074 // it means that they were doubled to indicate "&" instead of accelerator
1082 //-----------------------------------------------------------------------------
1084 //-----------------------------------------------------------------------------
1086 IMPLEMENT_DYNAMIC_CLASS(wxMenu
,wxEvtHandler
)
1090 m_accel
= gtk_accel_group_new();
1091 m_factory
= gtk_item_factory_new( GTK_TYPE_MENU
, "<main>", m_accel
);
1092 m_menu
= gtk_item_factory_get_widget( m_factory
, "<main>" );
1094 m_owner
= (GtkWidget
*) NULL
;
1096 // Tearoffs are entries, just like separators. So if we want this
1097 // menu to be a tear-off one, we just append a tearoff entry
1099 if(m_style
& wxMENU_TEAROFF
)
1101 GtkItemFactoryEntry entry
;
1102 entry
.path
= (char *)"/tearoff";
1103 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
1104 entry
.callback_action
= 0;
1105 entry
.item_type
= (char *)"<Tearoff>";
1106 entry
.accelerator
= (gchar
*) NULL
;
1107 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
1108 //GtkWidget *menuItem = gtk_item_factory_get_widget( m_factory, "<main>/tearoff" );
1111 // append the title as the very first entry if we have it
1114 Append(-2, m_title
);
1121 WX_CLEAR_LIST(wxMenuItemList
, m_items
);
1123 if ( GTK_IS_WIDGET( m_menu
))
1124 gtk_widget_destroy( m_menu
);
1126 gtk_object_unref( GTK_OBJECT(m_factory
) );
1129 bool wxMenu::GtkAppend(wxMenuItem
*mitem
)
1131 GtkWidget
*menuItem
;
1133 #if defined(USE_MENU_BITMAPS) || !GTK_CHECK_VERSION(1, 2, 0)
1134 bool appended
= FALSE
;
1137 // does this item terminate the current radio group?
1138 bool endOfRadioGroup
= TRUE
;
1140 if ( mitem
->IsSeparator() )
1142 GtkItemFactoryEntry entry
;
1143 entry
.path
= (char *)"/sep";
1144 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
1145 entry
.callback_action
= 0;
1146 entry
.item_type
= (char *)"<Separator>";
1147 entry
.accelerator
= (gchar
*) NULL
;
1149 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
1151 // this will be wrong for more than one separator. do we care?
1152 menuItem
= gtk_item_factory_get_widget( m_factory
, "<main>/sep" );
1154 // we might have a separator inside a radio group
1155 endOfRadioGroup
= FALSE
;
1157 else if ( mitem
->IsSubMenu() )
1159 // text has "_" instead of "&" after mitem->SetText()
1160 wxString
text( mitem
->GetText() );
1162 // local buffer in multibyte form
1165 strcat( buf
, wxGTK_CONV( text
) );
1167 GtkItemFactoryEntry entry
;
1169 entry
.callback
= (GtkItemFactoryCallback
) 0;
1170 entry
.callback_action
= 0;
1171 entry
.item_type
= (char *)"<Branch>";
1172 entry
.accelerator
= (gchar
*) NULL
;
1174 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
1176 wxString
path( mitem
->GetFactoryPath() );
1177 menuItem
= gtk_item_factory_get_item( m_factory
, wxGTK_CONV( path
) );
1179 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menuItem
), mitem
->GetSubMenu()->m_menu
);
1181 // if adding a submenu to a menu already existing in the menu bar, we
1182 // must set invoking window to allow processing events from this
1184 if ( m_invokingWindow
)
1185 wxMenubarSetInvokingWindow(mitem
->GetSubMenu(), m_invokingWindow
);
1187 #ifdef USE_MENU_BITMAPS
1188 else if (mitem
->GetBitmap().Ok()) // An item with bitmap
1190 wxString
text( mitem
->GetText() );
1191 const wxBitmap
*bitmap
= &mitem
->GetBitmap();
1193 menuItem
= gtk_pixmap_menu_item_new ();
1194 GtkWidget
*label
= gtk_accel_label_new ( wxGTK_CONV( text
) );
1195 gtk_misc_set_alignment (GTK_MISC (label
), 0.0, 0.5);
1196 gtk_container_add (GTK_CONTAINER (menuItem
), label
);
1197 gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (label
), menuItem
);
1199 GdkModifierType accel_mods
;
1201 // accelerator for the item, as specified by its label
1202 // (ex. Ctrl+O for open)
1203 gtk_accelerator_parse(GetHotKey(*mitem
).c_str(),
1204 &accel_key
, &accel_mods
);
1205 if (accel_key
!= GDK_VoidSymbol
)
1207 gtk_widget_add_accelerator (menuItem
,
1209 gtk_menu_get_accel_group(
1211 accel_key
, accel_mods
,
1215 // accelerator for the underlined char (ex ALT+F for the File menu)
1216 accel_key
= gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV( text
) );
1217 if (accel_key
!= GDK_VoidSymbol
)
1219 gtk_widget_add_accelerator (menuItem
,
1221 gtk_menu_ensure_uline_accel_group (
1227 gtk_widget_show (label
);
1229 mitem
->SetLabelWidget(label
);
1231 GtkWidget
* pixmap
= gtk_pixmap_new( bitmap
->GetPixmap(), bitmap
->GetMask() ? bitmap
->GetMask()->GetBitmap() : (GdkBitmap
* )NULL
);
1232 gtk_widget_show(pixmap
);
1233 gtk_pixmap_menu_item_set_pixmap(GTK_PIXMAP_MENU_ITEM( menuItem
), pixmap
);
1235 gtk_signal_connect( GTK_OBJECT(menuItem
), "activate",
1236 GTK_SIGNAL_FUNC(gtk_menu_clicked_callback
),
1239 gtk_menu_append( GTK_MENU(m_menu
), menuItem
);
1241 gtk_widget_show( menuItem
);
1243 appended
= TRUE
; // We've done this, don't do it again
1245 #endif // USE_MENU_BITMAPS
1246 else // a normal item
1248 // text has "_" instead of "&" after mitem->SetText() so don't use it
1249 wxString
text( mitem
->GetText() );
1251 // buffers containing the menu item path and type in multibyte form
1255 strcpy( bufPath
, "/" );
1256 strncat( bufPath
, wxGTK_CONV(text
), WXSIZEOF(bufPath
) - 2 );
1257 bufPath
[WXSIZEOF(bufPath
) - 1] = '\0';
1259 GtkItemFactoryEntry entry
;
1260 entry
.path
= bufPath
;
1261 entry
.callback
= (GtkItemFactoryCallback
) gtk_menu_clicked_callback
;
1262 entry
.callback_action
= 0;
1265 const char *item_type
;
1266 switch ( mitem
->GetKind() )
1269 item_type
= "<CheckItem>";
1273 if ( m_pathLastRadio
.empty() )
1275 // start of a new radio group
1276 item_type
= "<RadioItem>";
1277 wxString
tmp( wxGTK_CONV_BACK( bufPath
) );
1279 m_pathLastRadio
= tmp
;
1281 else // continue the radio group
1283 pathRadio
= m_pathLastRadio
;
1284 pathRadio
.Replace(wxT("_"), wxT(""));
1285 pathRadio
.Prepend(wxT("<main>/"));
1287 strncpy(bufType
, wxGTK_CONV(pathRadio
), WXSIZEOF(bufType
));
1288 bufType
[WXSIZEOF(bufType
) - 1] = '\0';
1289 item_type
= bufType
;
1292 // continue the existing radio group, if any
1293 endOfRadioGroup
= FALSE
;
1297 wxFAIL_MSG( _T("unexpected menu item kind") );
1301 item_type
= "<Item>";
1305 entry
.item_type
= (char *)item_type
; // cast needed for GTK+
1306 entry
.accelerator
= (gchar
*) NULL
;
1309 // due to an apparent bug in GTK+, we have to use a static buffer here -
1310 // otherwise GTK+ 1.2.2 manages to override the memory we pass to it
1312 char s_accel
[50]; // should be big enough, we check for overruns
1313 wxString
tmp( GetHotKey(*mitem
) );
1314 strncpy(s_accel
, wxGTK_CONV( tmp
), WXSIZEOF(s_accel
));
1315 s_accel
[WXSIZEOF(s_accel
) - 1] = '\0';
1316 entry
.accelerator
= s_accel
;
1317 #else // !wxUSE_ACCEL
1318 entry
.accelerator
= (char*) NULL
;
1319 #endif // wxUSE_ACCEL/!wxUSE_ACCEL
1321 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); /* what is 2 ? */
1323 wxString
path( mitem
->GetFactoryPath() );
1324 menuItem
= gtk_item_factory_get_widget( m_factory
, wxGTK_CONV( path
) );
1327 wxLogError( wxT("Wrong menu path: %s\n"), path
.c_str() );
1330 if ( !mitem
->IsSeparator() )
1332 wxASSERT_MSG( menuItem
, wxT("invalid menuitem") );
1334 gtk_signal_connect( GTK_OBJECT(menuItem
), "select",
1335 GTK_SIGNAL_FUNC(gtk_menu_hilight_callback
),
1338 gtk_signal_connect( GTK_OBJECT(menuItem
), "deselect",
1339 GTK_SIGNAL_FUNC(gtk_menu_nolight_callback
),
1343 mitem
->SetMenuItem(menuItem
);
1345 if ( endOfRadioGroup
)
1347 m_pathLastRadio
.clear();
1353 bool wxMenu::DoAppend(wxMenuItem
*mitem
)
1355 return GtkAppend(mitem
) && wxMenuBase::DoAppend(mitem
);
1358 bool wxMenu::DoInsert(size_t pos
, wxMenuItem
*item
)
1360 if ( !wxMenuBase::DoInsert(pos
, item
) )
1363 // GTK+ doesn't have a function to insert a menu using GtkItemFactory (as
1364 // of version 1.2.6), so we first append the item and then change its
1366 if ( !GtkAppend(item
) )
1369 if ( m_style
& wxMENU_TEAROFF
)
1371 // change the position as the first item is the tear-off marker
1375 GtkMenuShell
*menu_shell
= GTK_MENU_SHELL(m_factory
->widget
);
1376 gpointer data
= g_list_last(menu_shell
->children
)->data
;
1377 menu_shell
->children
= g_list_remove(menu_shell
->children
, data
);
1378 menu_shell
->children
= g_list_insert(menu_shell
->children
, data
, pos
);
1383 wxMenuItem
*wxMenu::DoRemove(wxMenuItem
*item
)
1385 if ( !wxMenuBase::DoRemove(item
) )
1386 return (wxMenuItem
*)NULL
;
1388 // TODO: this code doesn't delete the item factory item and this seems
1389 // impossible as of GTK 1.2.6.
1390 gtk_widget_destroy( item
->GetMenuItem() );
1395 int wxMenu::FindMenuIdByMenuItem( GtkWidget
*menuItem
) const
1397 wxMenuItemList::compatibility_iterator node
= m_items
.GetFirst();
1400 wxMenuItem
*item
= node
->GetData();
1401 if (item
->GetMenuItem() == menuItem
)
1402 return item
->GetId();
1403 node
= node
->GetNext();
1409 // ----------------------------------------------------------------------------
1411 // ----------------------------------------------------------------------------
1413 #if GTK_CHECK_VERSION(1, 2, 0) && wxUSE_ACCEL
1415 static wxString
GetHotKey( const wxMenuItem
& item
)
1419 wxAcceleratorEntry
*accel
= item
.GetAccel();
1422 int flags
= accel
->GetFlags();
1423 if ( flags
& wxACCEL_ALT
)
1424 hotkey
+= wxT("<alt>");
1425 if ( flags
& wxACCEL_CTRL
)
1426 hotkey
+= wxT("<control>");
1427 if ( flags
& wxACCEL_SHIFT
)
1428 hotkey
+= wxT("<shift>");
1430 int code
= accel
->GetKeyCode();
1445 hotkey
<< wxT('F') << code
- WXK_F1
+ 1;
1448 // TODO: we should use gdk_keyval_name() (a.k.a.
1449 // XKeysymToString) here as well as hardcoding the keysym
1450 // names this might be not portable
1451 case WXK_NUMPAD_INSERT
:
1452 hotkey
<< wxT("KP_Insert" );
1454 case WXK_NUMPAD_DELETE
:
1455 hotkey
<< wxT("KP_Delete" );
1458 hotkey
<< wxT("Insert" );
1461 hotkey
<< wxT("Delete" );
1464 hotkey
<< wxT("Up" );
1467 hotkey
<< wxT("Down" );
1470 hotkey
<< wxT("Prior" );
1473 hotkey
<< wxT("Next" );
1476 hotkey
<< wxT("Left" );
1479 hotkey
<< wxT("Right" );
1482 hotkey
<< wxT("Home" );
1485 hotkey
<< wxT("End" );
1488 hotkey
<< wxT("Return" );
1491 // if there are any other keys wxGetAccelFromString() may
1492 // return, we should process them here
1497 wxString name
= wxGTK_CONV_BACK( gdk_keyval_name((guint
)code
) );
1505 wxFAIL_MSG( wxT("unknown keyboard accel") );
1514 #endif // wxUSE_ACCEL
1517 //-----------------------------------------------------------------------------
1518 // substitute for missing GtkPixmapMenuItem
1519 //-----------------------------------------------------------------------------
1521 #ifdef USE_MENU_BITMAPS
1524 * Copyright (C) 1998, 1999, 2000 Free Software Foundation
1525 * All rights reserved.
1527 * This file is part of the Gnome Library.
1529 * The Gnome Library is free software; you can redistribute it and/or
1530 * modify it under the terms of the GNU Library General Public License as
1531 * published by the Free Software Foundation; either version 2 of the
1532 * License, or (at your option) any later version.
1534 * The Gnome Library is distributed in the hope that it will be useful,
1535 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1536 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1537 * Library General Public License for more details.
1539 * You should have received a copy of the GNU Library General Public
1540 * License along with the Gnome Library; see the file COPYING.LIB. If not,
1541 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
1542 * Boston, MA 02111-1307, USA.
1548 /* Author: Dietmar Maurer <dm@vlsivie.tuwien.ac.at> */
1550 #include <gtk/gtkaccellabel.h>
1551 #include <gtk/gtksignal.h>
1552 #include <gtk/gtkmenuitem.h>
1553 #include <gtk/gtkmenu.h>
1554 #include <gtk/gtkcontainer.h>
1559 static void gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass
*klass
);
1560 static void gtk_pixmap_menu_item_init (GtkPixmapMenuItem
*menu_item
);
1561 static void gtk_pixmap_menu_item_draw (GtkWidget
*widget
,
1562 GdkRectangle
*area
);
1563 static gint
gtk_pixmap_menu_item_expose (GtkWidget
*widget
,
1564 GdkEventExpose
*event
);
1566 /* we must override the following functions */
1568 static void gtk_pixmap_menu_item_map (GtkWidget
*widget
);
1569 static void gtk_pixmap_menu_item_size_allocate (GtkWidget
*widget
,
1570 GtkAllocation
*allocation
);
1571 static void gtk_pixmap_menu_item_forall (GtkContainer
*container
,
1572 gboolean include_internals
,
1573 GtkCallback callback
,
1574 gpointer callback_data
);
1575 static void gtk_pixmap_menu_item_size_request (GtkWidget
*widget
,
1576 GtkRequisition
*requisition
);
1577 static void gtk_pixmap_menu_item_remove (GtkContainer
*container
,
1580 static void changed_have_pixmap_status (GtkPixmapMenuItem
*menu_item
);
1582 static GtkMenuItemClass
*parent_class
= NULL
;
1586 #define BORDER_SPACING 3
1587 #define PMAP_WIDTH 20
1590 gtk_pixmap_menu_item_get_type (void)
1592 static GtkType pixmap_menu_item_type
= 0;
1594 if (!pixmap_menu_item_type
)
1596 GtkTypeInfo pixmap_menu_item_info
=
1598 (char *)"GtkPixmapMenuItem",
1599 sizeof (GtkPixmapMenuItem
),
1600 sizeof (GtkPixmapMenuItemClass
),
1601 (GtkClassInitFunc
) gtk_pixmap_menu_item_class_init
,
1602 (GtkObjectInitFunc
) gtk_pixmap_menu_item_init
,
1603 /* reserved_1 */ NULL
,
1604 /* reserved_2 */ NULL
,
1605 (GtkClassInitFunc
) NULL
,
1608 pixmap_menu_item_type
= gtk_type_unique (gtk_menu_item_get_type (),
1609 &pixmap_menu_item_info
);
1612 return pixmap_menu_item_type
;
1616 * gtk_pixmap_menu_item_new
1618 * Creates a new pixmap menu item. Use gtk_pixmap_menu_item_set_pixmap()
1619 * to set the pixmap wich is displayed at the left side.
1622 * &GtkWidget pointer to new menu item
1626 gtk_pixmap_menu_item_new (void)
1628 return GTK_WIDGET (gtk_type_new (gtk_pixmap_menu_item_get_type ()));
1632 gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass
*klass
)
1634 GtkObjectClass
*object_class
;
1635 GtkWidgetClass
*widget_class
;
1636 GtkMenuItemClass
*menu_item_class
;
1637 GtkContainerClass
*container_class
;
1639 object_class
= (GtkObjectClass
*) klass
;
1640 widget_class
= (GtkWidgetClass
*) klass
;
1641 menu_item_class
= (GtkMenuItemClass
*) klass
;
1642 container_class
= (GtkContainerClass
*) klass
;
1644 parent_class
= (GtkMenuItemClass
*) gtk_type_class (gtk_menu_item_get_type ());
1646 widget_class
->draw
= gtk_pixmap_menu_item_draw
;
1647 widget_class
->expose_event
= gtk_pixmap_menu_item_expose
;
1648 widget_class
->map
= gtk_pixmap_menu_item_map
;
1649 widget_class
->size_allocate
= gtk_pixmap_menu_item_size_allocate
;
1650 widget_class
->size_request
= gtk_pixmap_menu_item_size_request
;
1652 container_class
->forall
= gtk_pixmap_menu_item_forall
;
1653 container_class
->remove
= gtk_pixmap_menu_item_remove
;
1655 klass
->orig_toggle_size
= menu_item_class
->toggle_size
;
1656 klass
->have_pixmap_count
= 0;
1660 gtk_pixmap_menu_item_init (GtkPixmapMenuItem
*menu_item
)
1664 mi
= GTK_MENU_ITEM (menu_item
);
1666 menu_item
->pixmap
= NULL
;
1670 gtk_pixmap_menu_item_draw (GtkWidget
*widget
,
1673 g_return_if_fail (widget
!= NULL
);
1674 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
));
1675 g_return_if_fail (area
!= NULL
);
1677 if (GTK_WIDGET_CLASS (parent_class
)->draw
)
1678 (* GTK_WIDGET_CLASS (parent_class
)->draw
) (widget
, area
);
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
);
1687 gtk_pixmap_menu_item_expose (GtkWidget
*widget
,
1688 GdkEventExpose
*event
)
1690 g_return_val_if_fail (widget
!= NULL
, FALSE
);
1691 g_return_val_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
), FALSE
);
1692 g_return_val_if_fail (event
!= NULL
, FALSE
);
1694 if (GTK_WIDGET_CLASS (parent_class
)->expose_event
)
1695 (* GTK_WIDGET_CLASS (parent_class
)->expose_event
) (widget
, event
);
1697 if (GTK_WIDGET_DRAWABLE (widget
) &&
1698 GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
) {
1699 gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
),NULL
);
1706 * gtk_pixmap_menu_item_set_pixmap
1707 * @menu_item: Pointer to the pixmap menu item
1708 * @pixmap: Pointer to a pixmap widget
1710 * Set the pixmap of the menu item.
1715 gtk_pixmap_menu_item_set_pixmap (GtkPixmapMenuItem
*menu_item
,
1718 g_return_if_fail (menu_item
!= NULL
);
1719 g_return_if_fail (pixmap
!= NULL
);
1720 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (menu_item
));
1721 g_return_if_fail (GTK_IS_WIDGET (pixmap
));
1722 g_return_if_fail (menu_item
->pixmap
== NULL
);
1724 gtk_widget_set_parent (pixmap
, GTK_WIDGET (menu_item
));
1725 menu_item
->pixmap
= pixmap
;
1727 if (GTK_WIDGET_REALIZED (pixmap
->parent
) &&
1728 !GTK_WIDGET_REALIZED (pixmap
))
1729 gtk_widget_realize (pixmap
);
1731 if (GTK_WIDGET_VISIBLE (pixmap
->parent
)) {
1732 if (GTK_WIDGET_MAPPED (pixmap
->parent
) &&
1733 GTK_WIDGET_VISIBLE(pixmap
) &&
1734 !GTK_WIDGET_MAPPED (pixmap
))
1735 gtk_widget_map (pixmap
);
1738 changed_have_pixmap_status(menu_item
);
1740 if (GTK_WIDGET_VISIBLE (pixmap
) && GTK_WIDGET_VISIBLE (menu_item
))
1741 gtk_widget_queue_resize (pixmap
);
1745 gtk_pixmap_menu_item_map (GtkWidget
*widget
)
1747 GtkPixmapMenuItem
*menu_item
;
1749 g_return_if_fail (widget
!= NULL
);
1750 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
));
1752 menu_item
= GTK_PIXMAP_MENU_ITEM(widget
);
1754 GTK_WIDGET_CLASS(parent_class
)->map(widget
);
1756 if (menu_item
->pixmap
&&
1757 GTK_WIDGET_VISIBLE (menu_item
->pixmap
) &&
1758 !GTK_WIDGET_MAPPED (menu_item
->pixmap
))
1759 gtk_widget_map (menu_item
->pixmap
);
1763 gtk_pixmap_menu_item_size_allocate (GtkWidget
*widget
,
1764 GtkAllocation
*allocation
)
1766 GtkPixmapMenuItem
*pmenu_item
;
1768 pmenu_item
= GTK_PIXMAP_MENU_ITEM(widget
);
1770 if (pmenu_item
->pixmap
&& GTK_WIDGET_VISIBLE(pmenu_item
))
1772 GtkAllocation child_allocation
;
1775 border_width
= GTK_CONTAINER (widget
)->border_width
;
1777 child_allocation
.width
= pmenu_item
->pixmap
->requisition
.width
;
1778 child_allocation
.height
= pmenu_item
->pixmap
->requisition
.height
;
1779 child_allocation
.x
= border_width
+ BORDER_SPACING
;
1780 child_allocation
.y
= (border_width
+ BORDER_SPACING
1781 + (((allocation
->height
- child_allocation
.height
) - child_allocation
.x
)
1782 / 2)); /* center pixmaps vertically */
1783 gtk_widget_size_allocate (pmenu_item
->pixmap
, &child_allocation
);
1786 if (GTK_WIDGET_CLASS (parent_class
)->size_allocate
)
1787 GTK_WIDGET_CLASS(parent_class
)->size_allocate (widget
, allocation
);
1791 gtk_pixmap_menu_item_forall (GtkContainer
*container
,
1792 gboolean include_internals
,
1793 GtkCallback callback
,
1794 gpointer callback_data
)
1796 GtkPixmapMenuItem
*menu_item
;
1798 g_return_if_fail (container
!= NULL
);
1799 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container
));
1800 g_return_if_fail (callback
!= NULL
);
1802 menu_item
= GTK_PIXMAP_MENU_ITEM (container
);
1804 if (menu_item
->pixmap
)
1805 (* callback
) (menu_item
->pixmap
, callback_data
);
1807 GTK_CONTAINER_CLASS(parent_class
)->forall(container
,include_internals
,
1808 callback
,callback_data
);
1812 gtk_pixmap_menu_item_size_request (GtkWidget
*widget
,
1813 GtkRequisition
*requisition
)
1815 GtkPixmapMenuItem
*menu_item
;
1816 GtkRequisition req
= {0, 0};
1818 g_return_if_fail (widget
!= NULL
);
1819 g_return_if_fail (GTK_IS_MENU_ITEM (widget
));
1820 g_return_if_fail (requisition
!= NULL
);
1822 GTK_WIDGET_CLASS(parent_class
)->size_request(widget
,requisition
);
1824 menu_item
= GTK_PIXMAP_MENU_ITEM (widget
);
1826 if (menu_item
->pixmap
)
1827 gtk_widget_size_request(menu_item
->pixmap
, &req
);
1829 requisition
->height
= MAX(req
.height
+ GTK_CONTAINER(widget
)->border_width
+ BORDER_SPACING
, (unsigned int) requisition
->height
);
1830 requisition
->width
+= (req
.width
+ GTK_CONTAINER(widget
)->border_width
+ BORDER_SPACING
);
1834 gtk_pixmap_menu_item_remove (GtkContainer
*container
,
1838 gboolean widget_was_visible
;
1840 g_return_if_fail (container
!= NULL
);
1841 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container
));
1842 g_return_if_fail (child
!= NULL
);
1843 g_return_if_fail (GTK_IS_WIDGET (child
));
1845 bin
= GTK_BIN (container
);
1846 g_return_if_fail ((bin
->child
== child
||
1847 (GTK_PIXMAP_MENU_ITEM(container
)->pixmap
== child
)));
1849 widget_was_visible
= GTK_WIDGET_VISIBLE (child
);
1851 gtk_widget_unparent (child
);
1852 if (bin
->child
== child
)
1855 GTK_PIXMAP_MENU_ITEM(container
)->pixmap
= NULL
;
1856 changed_have_pixmap_status(GTK_PIXMAP_MENU_ITEM(container
));
1859 if (widget_was_visible
)
1860 gtk_widget_queue_resize (GTK_WIDGET (container
));
1864 /* important to only call this if there was actually a _change_ in pixmap == NULL */
1866 changed_have_pixmap_status (GtkPixmapMenuItem
*menu_item
)
1868 if (menu_item
->pixmap
!= NULL
) {
1869 GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
+= 1;
1871 if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
== 1) {
1872 /* Install pixmap toggle size */
1873 GTK_MENU_ITEM_GET_CLASS(menu_item
)->toggle_size
= MAX(GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->orig_toggle_size
, PMAP_WIDTH
);
1876 GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
-= 1;
1878 if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
== 0) {
1879 /* Install normal toggle size */
1880 GTK_MENU_ITEM_GET_CLASS(menu_item
)->toggle_size
= GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->orig_toggle_size
;
1884 /* Note that we actually need to do this for _all_ GtkPixmapMenuItem
1885 whenever the klass->toggle_size changes; but by doing it anytime
1886 this function is called, we get the same effect, just because of
1887 how the preferences option to show pixmaps works. Bogus, broken.
1889 if (GTK_WIDGET_VISIBLE(GTK_WIDGET(menu_item
)))
1890 gtk_widget_queue_resize(GTK_WIDGET(menu_item
));
1893 #endif // USE_MENU_BITMAPS