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 //-----------------------------------------------------------------------------
66 #define GTK_TYPE_PIXMAP_MENU_ITEM (gtk_pixmap_menu_item_get_type ())
67 #define GTK_PIXMAP_MENU_ITEM(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_PIXMAP_MENU_ITEM, GtkPixmapMenuItem))
68 #define GTK_PIXMAP_MENU_ITEM_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_PIXMAP_MENU_ITEM, GtkPixmapMenuItemClass))
69 #define GTK_IS_PIXMAP_MENU_ITEM(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_PIXMAP_MENU_ITEM))
70 #define GTK_IS_PIXMAP_MENU_ITEM_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PIXMAP_MENU_ITEM))
71 //#define GTK_PIXMAP_MENU_ITEM_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_PIXMAP_MENU_ITEM))
72 #define GTK_PIXMAP_MENU_ITEM_GET_CLASS(obj) (GTK_PIXMAP_MENU_ITEM_CLASS( GTK_OBJECT_GET_CLASS(obj)))
74 #ifndef GTK_MENU_ITEM_GET_CLASS
75 #define GTK_MENU_ITEM_GET_CLASS(obj) (GTK_MENU_ITEM_CLASS( GTK_OBJECT_GET_CLASS(obj)))
78 typedef struct _GtkPixmapMenuItem GtkPixmapMenuItem
;
79 typedef struct _GtkPixmapMenuItemClass GtkPixmapMenuItemClass
;
81 struct _GtkPixmapMenuItem
83 GtkMenuItem menu_item
;
88 struct _GtkPixmapMenuItemClass
90 GtkMenuItemClass parent_class
;
92 guint orig_toggle_size
;
93 guint have_pixmap_count
;
97 GtkType
gtk_pixmap_menu_item_get_type (void);
98 GtkWidget
* gtk_pixmap_menu_item_new (void);
99 void gtk_pixmap_menu_item_set_pixmap (GtkPixmapMenuItem
*menu_item
,
103 //-----------------------------------------------------------------------------
105 //-----------------------------------------------------------------------------
107 static wxString
wxReplaceUnderscore( const wxString
& title
)
111 /* GTK 1.2 wants to have "_" instead of "&" for accelerators */
114 while (*pc
!= wxT('\0'))
116 if ((*pc
== wxT('&')) && (*(pc
+1) == wxT('&')))
118 // "&" is doubled to indicate "&" instead of accelerator
122 else if (*pc
== wxT('&'))
124 #if GTK_CHECK_VERSION(1, 2, 0)
128 #if GTK_CHECK_VERSION(2, 0, 0)
129 else if (*pc
== wxT('/'))
133 else if (*pc
== wxT('\\'))
137 #elif GTK_CHECK_VERSION(1, 2, 0)
138 else if (*pc
== wxT('/'))
146 if ( *pc
== wxT('_') )
148 // underscores must be doubled to prevent them from being
149 // interpreted as accelerator character prefix by GTK
161 //-----------------------------------------------------------------------------
162 // activate message from GTK
163 //-----------------------------------------------------------------------------
165 static void gtk_menu_open_callback( GtkWidget
*widget
, wxMenu
*menu
)
167 if (g_isIdle
) wxapp_install_idle_handler();
169 wxMenuEvent
event( wxEVT_MENU_OPEN
, -1, menu
);
170 event
.SetEventObject( menu
);
172 wxEvtHandler
* handler
= menu
->GetEventHandler();
173 if (handler
&& handler
->ProcessEvent(event
))
176 wxWindow
*win
= menu
->GetInvokingWindow();
177 if (win
) win
->GetEventHandler()->ProcessEvent( event
);
180 //-----------------------------------------------------------------------------
182 //-----------------------------------------------------------------------------
184 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar
,wxWindow
)
186 wxMenuBar::wxMenuBar( long style
)
188 /* the parent window is known after wxFrame::SetMenu() */
189 m_needParent
= FALSE
;
191 m_invokingWindow
= (wxWindow
*) NULL
;
193 if (!PreCreation( (wxWindow
*) NULL
, wxDefaultPosition
, wxDefaultSize
) ||
194 !CreateBase( (wxWindow
*) NULL
, -1, wxDefaultPosition
, wxDefaultSize
, style
, wxDefaultValidator
, wxT("menubar") ))
196 wxFAIL_MSG( wxT("wxMenuBar creation failed") );
200 /* GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has. */
201 #if GTK_CHECK_VERSION(1, 2, 1)
202 m_accel
= gtk_accel_group_new();
203 m_factory
= gtk_item_factory_new( GTK_TYPE_MENU_BAR
, "<main>", m_accel
);
204 m_menubar
= gtk_item_factory_get_widget( m_factory
, "<main>" );
206 m_menubar
= gtk_menu_bar_new();
209 if (style
& wxMB_DOCKABLE
)
211 m_widget
= gtk_handle_box_new();
212 gtk_container_add( GTK_CONTAINER(m_widget
), GTK_WIDGET(m_menubar
) );
213 gtk_widget_show( GTK_WIDGET(m_menubar
) );
217 m_widget
= GTK_WIDGET(m_menubar
);
225 wxMenuBar::wxMenuBar()
227 /* the parent window is known after wxFrame::SetMenu() */
228 m_needParent
= FALSE
;
230 m_invokingWindow
= (wxWindow
*) NULL
;
232 if (!PreCreation( (wxWindow
*) NULL
, wxDefaultPosition
, wxDefaultSize
) ||
233 !CreateBase( (wxWindow
*) NULL
, -1, wxDefaultPosition
, wxDefaultSize
, 0, wxDefaultValidator
, wxT("menubar") ))
235 wxFAIL_MSG( wxT("wxMenuBar creation failed") );
239 /* GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has. */
240 #if GTK_CHECK_VERSION(1, 2, 1)
241 m_accel
= gtk_accel_group_new();
242 m_factory
= gtk_item_factory_new( GTK_TYPE_MENU_BAR
, "<main>", m_accel
);
243 m_menubar
= gtk_item_factory_get_widget( m_factory
, "<main>" );
245 m_menubar
= gtk_menu_bar_new();
248 m_widget
= GTK_WIDGET(m_menubar
);
255 wxMenuBar::~wxMenuBar()
257 // gtk_object_unref( GTK_OBJECT(m_factory) ); why not ?
260 static void wxMenubarUnsetInvokingWindow( wxMenu
*menu
, wxWindow
*win
)
262 menu
->SetInvokingWindow( (wxWindow
*) NULL
);
264 wxWindow
*top_frame
= win
;
265 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
266 top_frame
= top_frame
->GetParent();
268 /* support for native hot keys */
269 gtk_accel_group_detach( menu
->m_accel
, ACCEL_OBJ_CAST(top_frame
->m_widget
) );
271 wxMenuItemList::compatibility_iterator node
= menu
->GetMenuItems().GetFirst();
274 wxMenuItem
*menuitem
= node
->GetData();
275 if (menuitem
->IsSubMenu())
276 wxMenubarUnsetInvokingWindow( menuitem
->GetSubMenu(), win
);
277 node
= node
->GetNext();
281 static void wxMenubarSetInvokingWindow( wxMenu
*menu
, wxWindow
*win
)
283 menu
->SetInvokingWindow( win
);
285 #if GTK_CHECK_VERSION(1, 2, 1)
286 wxWindow
*top_frame
= win
;
287 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
288 top_frame
= top_frame
->GetParent();
290 /* support for native hot keys */
291 ACCEL_OBJECT
*obj
= ACCEL_OBJ_CAST(top_frame
->m_widget
);
292 if ( !g_slist_find( ACCEL_OBJECTS(menu
->m_accel
), obj
) )
293 gtk_accel_group_attach( menu
->m_accel
, obj
);
294 #endif // GTK+ 1.2.1+
296 wxMenuItemList::compatibility_iterator node
= menu
->GetMenuItems().GetFirst();
299 wxMenuItem
*menuitem
= node
->GetData();
300 if (menuitem
->IsSubMenu())
301 wxMenubarSetInvokingWindow( menuitem
->GetSubMenu(), win
);
302 node
= node
->GetNext();
306 void wxMenuBar::SetInvokingWindow( wxWindow
*win
)
308 m_invokingWindow
= win
;
309 #if GTK_CHECK_VERSION(1, 2, 1)
310 wxWindow
*top_frame
= win
;
311 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
312 top_frame
= top_frame
->GetParent();
314 /* support for native key accelerators indicated by underscroes */
315 ACCEL_OBJECT
*obj
= ACCEL_OBJ_CAST(top_frame
->m_widget
);
316 if ( !g_slist_find( ACCEL_OBJECTS(m_accel
), obj
) )
317 gtk_accel_group_attach( m_accel
, obj
);
318 #endif // GTK+ 1.2.1+
320 wxMenuList::compatibility_iterator node
= m_menus
.GetFirst();
323 wxMenu
*menu
= node
->GetData();
324 wxMenubarSetInvokingWindow( menu
, win
);
325 node
= node
->GetNext();
329 void wxMenuBar::UnsetInvokingWindow( wxWindow
*win
)
331 m_invokingWindow
= (wxWindow
*) NULL
;
332 #if GTK_CHECK_VERSION(1, 2, 1)
333 wxWindow
*top_frame
= win
;
334 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
335 top_frame
= top_frame
->GetParent();
337 // support for native key accelerators indicated by underscroes
338 gtk_accel_group_detach( m_accel
, ACCEL_OBJ_CAST(top_frame
->m_widget
) );
339 #endif // GTK+ 1.2.1+
341 wxMenuList::compatibility_iterator node
= m_menus
.GetFirst();
344 wxMenu
*menu
= node
->GetData();
345 wxMenubarUnsetInvokingWindow( menu
, win
);
346 node
= node
->GetNext();
350 bool wxMenuBar::Append( wxMenu
*menu
, const wxString
&title
)
352 if ( !wxMenuBarBase::Append( menu
, title
) )
355 return GtkAppend(menu
, title
);
358 bool wxMenuBar::GtkAppend(wxMenu
*menu
, const wxString
& title
)
360 wxString
str( wxReplaceUnderscore( title
) );
362 // This doesn't have much effect right now.
363 menu
->SetTitle( str
);
365 // GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has.
366 #if GTK_CHECK_VERSION(1, 2, 1)
369 buf
<< wxT('/') << str
.c_str();
371 // local buffer in multibyte form
373 strcpy(cbuf
, wxGTK_CONV(buf
) );
375 GtkItemFactoryEntry entry
;
376 entry
.path
= (gchar
*)cbuf
; // const_cast
377 entry
.accelerator
= (gchar
*) NULL
;
378 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
379 entry
.callback_action
= 0;
380 entry
.item_type
= (char *)"<Branch>";
382 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
383 // in order to get the pointer to the item we need the item text _without_ underscores
384 wxString tmp
= wxT("<main>/");
386 for ( pc
= str
; *pc
!= wxT('\0'); pc
++ )
388 // contrary to the common sense, we must throw out _all_ underscores,
389 // (i.e. "Hello__World" => "HelloWorld" and not "Hello_World" as we
390 // might naively think). IMHO it's a bug in GTK+ (VZ)
391 while (*pc
== wxT('_'))
395 menu
->m_owner
= gtk_item_factory_get_item( m_factory
, wxGTK_CONV( tmp
) );
396 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu
->m_owner
), menu
->m_menu
);
399 menu
->m_owner
= gtk_menu_item_new_with_label( wxGTK_CONV( str
) );
400 gtk_widget_show( menu
->m_owner
);
401 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu
->m_owner
), menu
->m_menu
);
403 gtk_menu_bar_append( GTK_MENU_BAR(m_menubar
), menu
->m_owner
);
407 gtk_signal_connect( GTK_OBJECT(menu
->m_owner
), "activate",
408 GTK_SIGNAL_FUNC(gtk_menu_open_callback
),
411 // m_invokingWindow is set after wxFrame::SetMenuBar(). This call enables
412 // addings menu later on.
413 if (m_invokingWindow
)
415 wxMenubarSetInvokingWindow( menu
, m_invokingWindow
);
417 // OPTIMISE ME: we should probably cache this, or pass it
418 // directly, but for now this is a minimal
419 // change to validate the new dynamic sizing.
420 // see (and refactor :) similar code in Remove
423 wxFrame
*frame
= wxDynamicCast( m_invokingWindow
, wxFrame
);
426 frame
->UpdateMenuBarSize();
432 bool wxMenuBar::Insert(size_t pos
, wxMenu
*menu
, const wxString
& title
)
434 if ( !wxMenuBarBase::Insert(pos
, menu
, title
) )
437 // GTK+ doesn't have a function to insert a menu using GtkItemFactory (as
438 // of version 1.2.6), so we first append the item and then change its
440 if ( !GtkAppend(menu
, title
) )
443 if (pos
+1 >= m_menus
.GetCount())
446 GtkMenuShell
*menu_shell
= GTK_MENU_SHELL(m_factory
->widget
);
447 gpointer data
= g_list_last(menu_shell
->children
)->data
;
448 menu_shell
->children
= g_list_remove(menu_shell
->children
, data
);
449 menu_shell
->children
= g_list_insert(menu_shell
->children
, data
, pos
);
454 wxMenu
*wxMenuBar::Replace(size_t pos
, wxMenu
*menu
, const wxString
& title
)
456 // remove the old item and insert a new one
457 wxMenu
*menuOld
= Remove(pos
);
458 if ( menuOld
&& !Insert(pos
, menu
, title
) )
460 return (wxMenu
*) NULL
;
463 // either Insert() succeeded or Remove() failed and menuOld is NULL
467 static wxMenu
*CopyMenu (wxMenu
*menu
)
469 wxMenu
*menucopy
= new wxMenu ();
470 wxMenuItemList::compatibility_iterator node
= menu
->GetMenuItems().GetFirst();
473 wxMenuItem
*item
= node
->GetData();
474 int itemid
= item
->GetId();
475 wxString text
= item
->GetText();
476 text
.Replace(wxT("_"), wxT("&"));
477 wxMenu
*submenu
= item
->GetSubMenu();
480 wxMenuItem
* itemcopy
= new wxMenuItem(menucopy
,
482 menu
->GetHelpString(itemid
));
483 itemcopy
->SetBitmap(item
->GetBitmap());
484 itemcopy
->SetCheckable(item
->IsCheckable());
485 menucopy
->Append(itemcopy
);
488 menucopy
->Append (itemid
, text
, CopyMenu(submenu
),
489 menu
->GetHelpString(itemid
));
491 node
= node
->GetNext();
497 wxMenu
*wxMenuBar::Remove(size_t pos
)
499 wxMenu
*menu
= wxMenuBarBase::Remove(pos
);
501 return (wxMenu
*) NULL
;
504 GtkMenuShell *menu_shell = GTK_MENU_SHELL(m_factory->widget);
506 printf( "factory entries before %d\n", (int)g_slist_length(m_factory->items) );
507 printf( "menu shell entries before %d\n", (int)g_list_length( menu_shell->children ) );
510 wxMenu
*menucopy
= CopyMenu( menu
);
512 // unparent calls unref() and that would delete the widget so we raise
513 // the ref count to 2 artificially before invoking unparent.
514 gtk_widget_ref( menu
->m_menu
);
515 gtk_widget_unparent( menu
->m_menu
);
517 gtk_widget_destroy( menu
->m_owner
);
522 printf( "factory entries after %d\n", (int)g_slist_length(m_factory->items) );
523 printf( "menu shell entries after %d\n", (int)g_list_length( menu_shell->children ) );
526 if (m_invokingWindow
)
528 // OPTIMISE ME: see comment in GtkAppend
530 wxFrame
*frame
= wxDynamicCast( m_invokingWindow
, wxFrame
);
533 frame
->UpdateMenuBarSize();
539 static int FindMenuItemRecursive( const wxMenu
*menu
, const wxString
&menuString
, const wxString
&itemString
)
541 if (wxMenuItem::GetLabelFromText(menu
->GetTitle()) == wxMenuItem::GetLabelFromText(menuString
))
543 int res
= menu
->FindItem( itemString
);
544 if (res
!= wxNOT_FOUND
)
548 wxMenuItemList::compatibility_iterator node
= menu
->GetMenuItems().GetFirst();
551 wxMenuItem
*item
= node
->GetData();
552 if (item
->IsSubMenu())
553 return FindMenuItemRecursive(item
->GetSubMenu(), menuString
, itemString
);
555 node
= node
->GetNext();
561 int wxMenuBar::FindMenuItem( const wxString
&menuString
, const wxString
&itemString
) const
563 wxMenuList::compatibility_iterator node
= m_menus
.GetFirst();
566 wxMenu
*menu
= node
->GetData();
567 int res
= FindMenuItemRecursive( menu
, menuString
, itemString
);
570 node
= node
->GetNext();
576 // Find a wxMenuItem using its id. Recurses down into sub-menus
577 static wxMenuItem
* FindMenuItemByIdRecursive(const wxMenu
* menu
, int id
)
579 wxMenuItem
* result
= menu
->FindChildItem(id
);
581 wxMenuItemList::compatibility_iterator node
= menu
->GetMenuItems().GetFirst();
582 while ( node
&& result
== NULL
)
584 wxMenuItem
*item
= node
->GetData();
585 if (item
->IsSubMenu())
587 result
= FindMenuItemByIdRecursive( item
->GetSubMenu(), id
);
589 node
= node
->GetNext();
595 wxMenuItem
* wxMenuBar::FindItem( int id
, wxMenu
**menuForItem
) const
597 wxMenuItem
* result
= 0;
598 wxMenuList::compatibility_iterator node
= m_menus
.GetFirst();
599 while (node
&& result
== 0)
601 wxMenu
*menu
= node
->GetData();
602 result
= FindMenuItemByIdRecursive( menu
, id
);
603 node
= node
->GetNext();
608 *menuForItem
= result
? result
->GetMenu() : (wxMenu
*)NULL
;
614 void wxMenuBar::EnableTop( size_t pos
, bool flag
)
616 wxMenuList::compatibility_iterator node
= m_menus
.Item( pos
);
618 wxCHECK_RET( node
, wxT("menu not found") );
620 wxMenu
* menu
= node
->GetData();
623 gtk_widget_set_sensitive( menu
->m_owner
, flag
);
626 wxString
wxMenuBar::GetLabelTop( size_t pos
) const
628 wxMenuList::compatibility_iterator node
= m_menus
.Item( pos
);
630 wxCHECK_MSG( node
, wxT("invalid"), wxT("menu not found") );
632 wxMenu
* menu
= node
->GetData();
635 wxString
text( menu
->GetTitle() );
636 for ( const wxChar
*pc
= text
.c_str(); *pc
; pc
++ )
638 if ( *pc
== wxT('_') )
640 // '_' is the escape character for GTK+
644 // don't remove ampersands '&' since if we have them in the menu title
645 // it means that they were doubled to indicate "&" instead of accelerator
653 void wxMenuBar::SetLabelTop( size_t pos
, const wxString
& label
)
655 wxMenuList::compatibility_iterator node
= m_menus
.Item( pos
);
657 wxCHECK_RET( node
, wxT("menu not found") );
659 wxMenu
* menu
= node
->GetData();
661 wxString
str( wxReplaceUnderscore( label
) );
663 menu
->SetTitle( str
);
667 GtkLabel
*label
= GTK_LABEL( GTK_BIN(menu
->m_owner
)->child
);
670 gtk_label_set( label
, wxGTK_CONV( str
) );
672 /* reparse key accel */
673 (void)gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV( str
) );
674 gtk_accel_label_refetch( GTK_ACCEL_LABEL(label
) );
679 //-----------------------------------------------------------------------------
681 //-----------------------------------------------------------------------------
683 static void gtk_menu_clicked_callback( GtkWidget
*widget
, wxMenu
*menu
)
686 wxapp_install_idle_handler();
688 int id
= menu
->FindMenuIdByMenuItem(widget
);
690 /* should find it for normal (not popup) menu */
691 wxASSERT_MSG( (id
!= -1) || (menu
->GetInvokingWindow() != NULL
),
692 _T("menu item not found in gtk_menu_clicked_callback") );
694 if (!menu
->IsEnabled(id
))
697 wxMenuItem
* item
= menu
->FindChildItem( id
);
698 wxCHECK_RET( item
, wxT("error in menu item callback") );
700 if (item
->IsCheckable())
702 bool isReallyChecked
= item
->IsChecked(),
703 isInternallyChecked
= item
->wxMenuItemBase::IsChecked();
705 // ensure that the internal state is always consistent with what is
706 // shown on the screen
707 item
->wxMenuItemBase::Check(isReallyChecked
);
709 // we must not report the events for the radio button going up nor the
710 // events resulting from the calls to wxMenuItem::Check()
711 if ( (item
->GetKind() == wxITEM_RADIO
&& !isReallyChecked
) ||
712 (isInternallyChecked
== isReallyChecked
) )
719 // Is this menu on a menubar? (possibly nested)
720 wxFrame
* frame
= NULL
;
722 while ( pm
&& !frame
)
724 if ( pm
->IsAttached() )
725 frame
= pm
->GetMenuBar()->GetFrame();
726 pm
= pm
->GetParent();
729 // FIXME: why do we have to call wxFrame::GetEventHandler() directly here?
730 // normally wxMenu::SendEvent() should be enough, if it doesn't work
731 // in wxGTK then we have a bug in wxMenu::GetInvokingWindow() which
732 // should be fixed instead of working around it here...
735 // If it is attached then let the frame send the event.
736 // Don't call frame->ProcessCommand(id) because it toggles
737 // checkable items and we've already done that above.
738 wxCommandEvent
commandEvent(wxEVT_COMMAND_MENU_SELECTED
, id
);
739 commandEvent
.SetEventObject(frame
);
740 if (item
->IsCheckable())
741 commandEvent
.SetInt(item
->IsChecked());
742 commandEvent
.SetEventObject(menu
);
744 frame
->GetEventHandler()->ProcessEvent(commandEvent
);
748 // otherwise let the menu have it
749 menu
->SendEvent(id
, item
->IsCheckable() ? item
->IsChecked() : -1);
753 //-----------------------------------------------------------------------------
755 //-----------------------------------------------------------------------------
757 static void gtk_menu_hilight_callback( GtkWidget
*widget
, wxMenu
*menu
)
759 if (g_isIdle
) wxapp_install_idle_handler();
761 int id
= menu
->FindMenuIdByMenuItem(widget
);
763 wxASSERT( id
!= -1 ); // should find it!
765 if (!menu
->IsEnabled(id
))
768 wxMenuEvent
event( wxEVT_MENU_HIGHLIGHT
, id
);
769 event
.SetEventObject( menu
);
771 wxEvtHandler
* handler
= menu
->GetEventHandler();
772 if (handler
&& handler
->ProcessEvent(event
))
775 wxWindow
*win
= menu
->GetInvokingWindow();
776 if (win
) win
->GetEventHandler()->ProcessEvent( event
);
779 //-----------------------------------------------------------------------------
781 //-----------------------------------------------------------------------------
783 static void gtk_menu_nolight_callback( GtkWidget
*widget
, wxMenu
*menu
)
785 if (g_isIdle
) wxapp_install_idle_handler();
787 int id
= menu
->FindMenuIdByMenuItem(widget
);
789 wxASSERT( id
!= -1 ); // should find it!
791 if (!menu
->IsEnabled(id
))
794 wxMenuEvent
event( wxEVT_MENU_HIGHLIGHT
, -1 );
795 event
.SetEventObject( menu
);
797 wxEvtHandler
* handler
= menu
->GetEventHandler();
798 if (handler
&& handler
->ProcessEvent(event
))
801 wxWindow
*win
= menu
->GetInvokingWindow();
803 win
->GetEventHandler()->ProcessEvent( event
);
806 //-----------------------------------------------------------------------------
808 //-----------------------------------------------------------------------------
810 IMPLEMENT_DYNAMIC_CLASS(wxMenuItem
, wxObject
)
812 wxMenuItem
*wxMenuItemBase::New(wxMenu
*parentMenu
,
814 const wxString
& name
,
815 const wxString
& help
,
819 return new wxMenuItem(parentMenu
, id
, name
, help
, kind
, subMenu
);
822 wxMenuItem::wxMenuItem(wxMenu
*parentMenu
,
824 const wxString
& text
,
825 const wxString
& help
,
828 : wxMenuItemBase(parentMenu
, id
, text
, help
, kind
, subMenu
)
833 wxMenuItem::wxMenuItem(wxMenu
*parentMenu
,
835 const wxString
& text
,
836 const wxString
& help
,
839 : wxMenuItemBase(parentMenu
, id
, text
, help
,
840 isCheckable
? wxITEM_CHECK
: wxITEM_NORMAL
, subMenu
)
845 void wxMenuItem::Init(const wxString
& text
)
847 m_labelWidget
= (GtkWidget
*) NULL
;
848 m_menuItem
= (GtkWidget
*) NULL
;
853 wxMenuItem::~wxMenuItem()
855 // don't delete menu items, the menus take care of that
858 // return the menu item text without any menu accels
860 wxString
wxMenuItemBase::GetLabelFromText(const wxString
& text
)
864 for ( const wxChar
*pc
= text
.c_str(); *pc
; pc
++ )
866 if ( *pc
== wxT('_') )
868 // GTK 1.2 escapes "xxx_xxx" to "xxx__xxx"
874 #if GTK_CHECK_VERSION(2, 0, 0)
875 if ( *pc
== wxT('\\') )
877 // GTK 2.0 escapes "xxx/xxx" to "xxx\/xxx"
884 if ( (*pc
== wxT('&')) && (*(pc
+1) != wxT('&')) )
887 // "&" is doubled to indicate "&" instead of accelerator
894 // wxPrintf( L"text %s label %s\n", text.c_str(), label.c_str() );
899 void wxMenuItem::SetText( const wxString
& str
)
901 // Some optimization to avoid flicker
902 wxString oldLabel
= m_text
;
903 oldLabel
= wxStripMenuCodes(oldLabel
.BeforeFirst('\t'));
904 oldLabel
.Replace(wxT("_"), wxT(""));
905 wxString label1
= wxStripMenuCodes(str
.BeforeFirst('\t'));
906 if (oldLabel
== label1
)
915 label
= (GtkLabel
*) m_labelWidget
;
917 label
= GTK_LABEL( GTK_BIN(m_menuItem
)->child
);
919 #if GTK_CHECK_VERSION(2, 0, 0)
920 // We have to imitate item_factory_unescape_label here
922 for (size_t n
= 0; n
< m_text
.Len(); n
++)
924 if (m_text
[n
] != wxT('\\'))
928 gtk_label_set_text_with_mnemonic( GTK_LABEL(label
), wxGTK_CONV(tmp
) );
931 gtk_label_set( label
, wxGTK_CONV( m_text
) );
934 (void)gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV(m_text
) );
935 gtk_accel_label_refetch( GTK_ACCEL_LABEL(label
) );
940 // it's valid for this function to be called even if m_menuItem == NULL
941 void wxMenuItem::DoSetText( const wxString
& str
)
943 // '\t' is the deliminator indicating a hot key
945 const wxChar
*pc
= str
;
946 while ( (*pc
!= wxT('\0')) && (*pc
!= wxT('\t')) )
948 if ((*pc
== wxT('&')) && (*(pc
+1) == wxT('&')))
950 // "&" is doubled to indicate "&" instead of accelerator
954 else if (*pc
== wxT('&'))
958 #if GTK_CHECK_VERSION(2, 0, 0)
959 else if ( *pc
== wxT('_') ) // escape underscores
961 // m_text << wxT("__"); doesn't work
964 else if (*pc
== wxT('/')) // we have to escape slashes
966 m_text
<< wxT("\\/");
968 else if (*pc
== wxT('\\')) // we have to double backslashes
970 m_text
<< wxT("\\\\");
973 else if ( *pc
== wxT('_') ) // escape underscores
977 else if (*pc
== wxT('/')) /* we have to filter out slashes ... */
979 m_text
<< wxT('\\'); /* ... and replace them with back slashes */
988 // wxPrintf( L"str %s m_text %s\n", str.c_str(), m_text.c_str() );
1001 wxAcceleratorEntry
*wxMenuItem::GetAccel() const
1006 return (wxAcceleratorEntry
*)NULL
;
1009 // as wxGetAccelFromString() looks for TAB, insert a dummy one here
1011 label
<< wxT('\t') << GetHotKey();
1013 return wxGetAccelFromString(label
);
1016 #endif // wxUSE_ACCEL
1018 void wxMenuItem::Check( bool check
)
1020 wxCHECK_RET( m_menuItem
, wxT("invalid menu item") );
1022 if (check
== m_isChecked
)
1025 wxMenuItemBase::Check( check
);
1027 switch ( GetKind() )
1031 gtk_check_menu_item_set_state( (GtkCheckMenuItem
*)m_menuItem
, (gint
)check
);
1035 wxFAIL_MSG( _T("can't check this item") );
1039 void wxMenuItem::Enable( bool enable
)
1041 wxCHECK_RET( m_menuItem
, wxT("invalid menu item") );
1043 gtk_widget_set_sensitive( m_menuItem
, enable
);
1044 wxMenuItemBase::Enable( enable
);
1047 bool wxMenuItem::IsChecked() const
1049 wxCHECK_MSG( m_menuItem
, FALSE
, wxT("invalid menu item") );
1051 wxCHECK_MSG( IsCheckable(), FALSE
,
1052 wxT("can't get state of uncheckable item!") );
1054 return ((GtkCheckMenuItem
*)m_menuItem
)->active
!= 0;
1057 wxString
wxMenuItem::GetFactoryPath() const
1059 // In order to get the pointer to the item we need the item
1060 // text _without_ underscores in GTK 1.2
1061 wxString
path( wxT("<main>/") );
1063 for ( const wxChar
*pc
= m_text
.c_str(); *pc
; pc
++ )
1065 if ( *pc
== wxT('_') )
1070 // remove '_' unconditionally
1075 // don't remove ampersands '&' since if we have them in the menu item title
1076 // it means that they were doubled to indicate "&" instead of accelerator
1084 //-----------------------------------------------------------------------------
1086 //-----------------------------------------------------------------------------
1088 IMPLEMENT_DYNAMIC_CLASS(wxMenu
,wxEvtHandler
)
1092 m_accel
= gtk_accel_group_new();
1093 m_factory
= gtk_item_factory_new( GTK_TYPE_MENU
, "<main>", m_accel
);
1094 m_menu
= gtk_item_factory_get_widget( m_factory
, "<main>" );
1096 m_owner
= (GtkWidget
*) NULL
;
1098 // Tearoffs are entries, just like separators. So if we want this
1099 // menu to be a tear-off one, we just append a tearoff entry
1101 if(m_style
& wxMENU_TEAROFF
)
1103 GtkItemFactoryEntry entry
;
1104 entry
.path
= (char *)"/tearoff";
1105 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
1106 entry
.callback_action
= 0;
1107 entry
.item_type
= (char *)"<Tearoff>";
1108 entry
.accelerator
= (gchar
*) NULL
;
1109 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
1110 //GtkWidget *menuItem = gtk_item_factory_get_widget( m_factory, "<main>/tearoff" );
1113 // append the title as the very first entry if we have it
1116 Append(-2, m_title
);
1123 WX_CLEAR_LIST(wxMenuItemList
, m_items
);
1125 if ( GTK_IS_WIDGET( m_menu
))
1126 gtk_widget_destroy( m_menu
);
1128 gtk_object_unref( GTK_OBJECT(m_factory
) );
1131 bool wxMenu::GtkAppend(wxMenuItem
*mitem
)
1133 GtkWidget
*menuItem
;
1135 // does this item terminate the current radio group?
1136 bool endOfRadioGroup
= TRUE
;
1138 if ( mitem
->IsSeparator() )
1140 GtkItemFactoryEntry entry
;
1141 entry
.path
= (char *)"/sep";
1142 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
1143 entry
.callback_action
= 0;
1144 entry
.item_type
= (char *)"<Separator>";
1145 entry
.accelerator
= (gchar
*) NULL
;
1147 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
1149 // this will be wrong for more than one separator. do we care?
1150 menuItem
= gtk_item_factory_get_widget( m_factory
, "<main>/sep" );
1152 // we might have a separator inside a radio group
1153 endOfRadioGroup
= FALSE
;
1155 else if ( mitem
->IsSubMenu() )
1157 // text has "_" instead of "&" after mitem->SetText()
1158 wxString
text( mitem
->GetText() );
1160 // local buffer in multibyte form
1163 strcat( buf
, wxGTK_CONV( text
) );
1165 GtkItemFactoryEntry entry
;
1167 entry
.callback
= (GtkItemFactoryCallback
) 0;
1168 entry
.callback_action
= 0;
1169 entry
.item_type
= (char *)"<Branch>";
1170 entry
.accelerator
= (gchar
*) NULL
;
1172 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); // what is 2 ?
1174 wxString
path( mitem
->GetFactoryPath() );
1175 menuItem
= gtk_item_factory_get_item( m_factory
, wxGTK_CONV( path
) );
1177 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menuItem
), mitem
->GetSubMenu()->m_menu
);
1179 // if adding a submenu to a menu already existing in the menu bar, we
1180 // must set invoking window to allow processing events from this
1182 if ( m_invokingWindow
)
1183 wxMenubarSetInvokingWindow(mitem
->GetSubMenu(), m_invokingWindow
);
1186 else if (mitem
->GetBitmap().Ok())
1188 // Our extra code for Bitmaps in GTK 1.2
1189 wxString
text( mitem
->GetText() );
1190 const wxBitmap
*bitmap
= &mitem
->GetBitmap();
1192 menuItem
= gtk_pixmap_menu_item_new ();
1193 GtkWidget
*label
= gtk_accel_label_new ( wxGTK_CONV( text
) );
1194 gtk_misc_set_alignment (GTK_MISC (label
), 0.0, 0.5);
1195 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(), &accel_key
,
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 (
1223 accel_key
, (GdkModifierType
) 0,
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
);
1240 gtk_widget_show( menuItem
);
1243 else // a normal item
1245 // text has "_" instead of "&" after mitem->SetText() so don't use it
1246 wxString
text( mitem
->GetText() );
1248 // buffers containing the menu item path and type in multibyte form
1252 strcpy( bufPath
, "/" );
1253 strncat( bufPath
, wxGTK_CONV(text
), WXSIZEOF(bufPath
) - 2 );
1254 bufPath
[WXSIZEOF(bufPath
) - 1] = '\0';
1256 GtkItemFactoryEntry entry
;
1257 entry
.path
= bufPath
;
1258 entry
.callback
= (GtkItemFactoryCallback
) gtk_menu_clicked_callback
;
1259 entry
.callback_action
= 0;
1262 const char *item_type
;
1263 switch ( mitem
->GetKind() )
1266 item_type
= "<CheckItem>";
1270 if ( m_pathLastRadio
.empty() )
1272 // start of a new radio group
1273 item_type
= "<RadioItem>";
1274 wxString
tmp( wxGTK_CONV_BACK( bufPath
) );
1276 m_pathLastRadio
= tmp
;
1278 else // continue the radio group
1280 pathRadio
= m_pathLastRadio
;
1281 pathRadio
.Replace(wxT("_"), wxT(""));
1282 pathRadio
.Prepend(wxT("<main>/"));
1284 strncpy(bufType
, wxGTK_CONV(pathRadio
), WXSIZEOF(bufType
));
1285 bufType
[WXSIZEOF(bufType
) - 1] = '\0';
1286 item_type
= bufType
;
1289 // continue the existing radio group, if any
1290 endOfRadioGroup
= FALSE
;
1294 wxFAIL_MSG( _T("unexpected menu item kind") );
1298 item_type
= "<Item>";
1299 #if defined(__WXGTK20__) && wxUSE_IMAGE
1300 if (mitem
->GetBitmap().Ok())
1302 item_type
= "<ImageItem>";
1303 // GTK2's image factory know about image items, but they need to
1304 // get a GdkPixbuf structure, which we need to create on the fly.
1305 // This Pixbuf structure needs to be static so we create it and
1306 // just make it a memory leak...
1307 wxImage
image( mitem
->GetBitmap().ConvertToImage() );
1308 size_t size
= 4 + // magic
1310 image
.GetHeight() * image
.GetWidth() * 4; // RGBA
1312 unsigned char *dest
= new unsigned char[size
];
1313 entry
.extra_data
= dest
;
1315 unsigned char *source
= image
.GetData();
1316 bool has_mask
= image
.HasMask();
1317 unsigned char mask_r
= image
.GetMaskRed();
1318 unsigned char mask_b
= image
.GetMaskBlue();
1319 unsigned char mask_g
= image
.GetMaskGreen();
1323 *dest
= 'G'; dest
++; *dest
= 'd'; dest
++; *dest
= 'k'; dest
++; *dest
= 'P'; dest
++;
1326 *dest
= tmp
>> 24; dest
++; *dest
= tmp
>> 16; dest
++; *dest
= tmp
>> 8; dest
++; *dest
= tmp
; dest
++;
1328 *dest
= 1; dest
++; *dest
= 1; dest
++; *dest
= 0; dest
++; *dest
= 2; dest
++;
1330 tmp
= image
.GetWidth()*4;
1331 *dest
= tmp
>> 24; dest
++; *dest
= tmp
>> 16; dest
++; *dest
= tmp
>> 8; dest
++; *dest
= tmp
; dest
++;
1333 tmp
= image
.GetWidth();
1334 *dest
= tmp
>> 24; dest
++; *dest
= tmp
>> 16; dest
++; *dest
= tmp
>> 8; dest
++; *dest
= tmp
; dest
++;
1336 tmp
= image
.GetHeight();
1337 *dest
= tmp
>> 24; dest
++; *dest
= tmp
>> 16; dest
++; *dest
= tmp
>> 8; dest
++; *dest
= tmp
; dest
++;
1339 for (int i
= 0; i
< image
.GetWidth()*image
.GetHeight(); i
++)
1341 unsigned char r
= *source
; source
++;
1342 unsigned char g
= *source
; source
++;
1343 unsigned char b
= *source
; source
++;
1347 if (has_mask
&& (r
== mask_r
) && (g
== mask_g
) && (b
== mask_b
))
1359 entry
.item_type
= (char *)item_type
; // cast needed for GTK+
1360 entry
.accelerator
= (gchar
*) NULL
;
1363 // due to an apparent bug in GTK+, we have to use a static buffer here -
1364 // otherwise GTK+ 1.2.2 manages to override the memory we pass to it
1366 char s_accel
[50]; // should be big enough, we check for overruns
1367 wxString
tmp( GetHotKey(*mitem
) );
1368 strncpy(s_accel
, wxGTK_CONV( tmp
), WXSIZEOF(s_accel
));
1369 s_accel
[WXSIZEOF(s_accel
) - 1] = '\0';
1370 entry
.accelerator
= s_accel
;
1371 #else // !wxUSE_ACCEL
1372 entry
.accelerator
= (char*) NULL
;
1373 #endif // wxUSE_ACCEL/!wxUSE_ACCEL
1375 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); /* what is 2 ? */
1377 wxString
path( mitem
->GetFactoryPath() );
1378 menuItem
= gtk_item_factory_get_widget( m_factory
, wxGTK_CONV( path
) );
1381 wxLogError( wxT("Wrong menu path: %s\n"), path
.c_str() );
1384 if ( !mitem
->IsSeparator() )
1386 wxASSERT_MSG( menuItem
, wxT("invalid menuitem") );
1388 gtk_signal_connect( GTK_OBJECT(menuItem
), "select",
1389 GTK_SIGNAL_FUNC(gtk_menu_hilight_callback
),
1392 gtk_signal_connect( GTK_OBJECT(menuItem
), "deselect",
1393 GTK_SIGNAL_FUNC(gtk_menu_nolight_callback
),
1397 mitem
->SetMenuItem(menuItem
);
1399 if ( endOfRadioGroup
)
1401 m_pathLastRadio
.clear();
1407 wxMenuItem
* wxMenu::DoAppend(wxMenuItem
*mitem
)
1409 if (!GtkAppend(mitem
))
1411 return wxMenuBase::DoAppend(mitem
);
1414 wxMenuItem
* wxMenu::DoInsert(size_t pos
, wxMenuItem
*item
)
1416 if ( !wxMenuBase::DoInsert(pos
, item
) )
1419 // GTK+ doesn't have a function to insert a menu using GtkItemFactory (as
1420 // of version 1.2.6), so we first append the item and then change its
1422 if ( !GtkAppend(item
) )
1425 if ( m_style
& wxMENU_TEAROFF
)
1427 // change the position as the first item is the tear-off marker
1431 GtkMenuShell
*menu_shell
= GTK_MENU_SHELL(m_factory
->widget
);
1432 gpointer data
= g_list_last(menu_shell
->children
)->data
;
1433 menu_shell
->children
= g_list_remove(menu_shell
->children
, data
);
1434 menu_shell
->children
= g_list_insert(menu_shell
->children
, data
, pos
);
1439 wxMenuItem
*wxMenu::DoRemove(wxMenuItem
*item
)
1441 if ( !wxMenuBase::DoRemove(item
) )
1442 return (wxMenuItem
*)NULL
;
1444 // TODO: this code doesn't delete the item factory item and this seems
1445 // impossible as of GTK 1.2.6.
1446 gtk_widget_destroy( item
->GetMenuItem() );
1451 int wxMenu::FindMenuIdByMenuItem( GtkWidget
*menuItem
) const
1453 wxMenuItemList::compatibility_iterator node
= m_items
.GetFirst();
1456 wxMenuItem
*item
= node
->GetData();
1457 if (item
->GetMenuItem() == menuItem
)
1458 return item
->GetId();
1459 node
= node
->GetNext();
1465 // ----------------------------------------------------------------------------
1467 // ----------------------------------------------------------------------------
1469 #if GTK_CHECK_VERSION(1, 2, 0) && wxUSE_ACCEL
1471 static wxString
GetHotKey( const wxMenuItem
& item
)
1475 wxAcceleratorEntry
*accel
= item
.GetAccel();
1478 int flags
= accel
->GetFlags();
1479 if ( flags
& wxACCEL_ALT
)
1480 hotkey
+= wxT("<alt>");
1481 if ( flags
& wxACCEL_CTRL
)
1482 hotkey
+= wxT("<control>");
1483 if ( flags
& wxACCEL_SHIFT
)
1484 hotkey
+= wxT("<shift>");
1486 int code
= accel
->GetKeyCode();
1501 hotkey
<< wxT('F') << code
- WXK_F1
+ 1;
1504 // TODO: we should use gdk_keyval_name() (a.k.a.
1505 // XKeysymToString) here as well as hardcoding the keysym
1506 // names this might be not portable
1507 case WXK_NUMPAD_INSERT
:
1508 hotkey
<< wxT("KP_Insert" );
1510 case WXK_NUMPAD_DELETE
:
1511 hotkey
<< wxT("KP_Delete" );
1514 hotkey
<< wxT("Insert" );
1517 hotkey
<< wxT("Delete" );
1520 hotkey
<< wxT("Up" );
1523 hotkey
<< wxT("Down" );
1527 hotkey
<< wxT("Prior" );
1531 hotkey
<< wxT("Next" );
1534 hotkey
<< wxT("Left" );
1537 hotkey
<< wxT("Right" );
1540 hotkey
<< wxT("Home" );
1543 hotkey
<< wxT("End" );
1546 hotkey
<< wxT("Return" );
1549 // if there are any other keys wxGetAccelFromString() may
1550 // return, we should process them here
1555 wxString name
= wxGTK_CONV_BACK( gdk_keyval_name((guint
)code
) );
1563 wxFAIL_MSG( wxT("unknown keyboard accel") );
1572 #endif // wxUSE_ACCEL
1575 //-----------------------------------------------------------------------------
1576 // substitute for missing GtkPixmapMenuItem
1577 //-----------------------------------------------------------------------------
1582 * Copyright (C) 1998, 1999, 2000 Free Software Foundation
1583 * All rights reserved.
1585 * This file is part of the Gnome Library.
1587 * The Gnome Library is free software; you can redistribute it and/or
1588 * modify it under the terms of the GNU Library General Public License as
1589 * published by the Free Software Foundation; either version 2 of the
1590 * License, or (at your option) any later version.
1592 * The Gnome Library is distributed in the hope that it will be useful,
1593 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1594 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1595 * Library General Public License for more details.
1597 * You should have received a copy of the GNU Library General Public
1598 * License along with the Gnome Library; see the file COPYING.LIB. If not,
1599 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
1600 * Boston, MA 02111-1307, USA.
1606 /* Author: Dietmar Maurer <dm@vlsivie.tuwien.ac.at> */
1608 #include <gtk/gtkaccellabel.h>
1609 #include <gtk/gtksignal.h>
1610 #include <gtk/gtkmenuitem.h>
1611 #include <gtk/gtkmenu.h>
1612 #include <gtk/gtkcontainer.h>
1617 static void gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass
*klass
);
1618 static void gtk_pixmap_menu_item_init (GtkPixmapMenuItem
*menu_item
);
1619 static void gtk_pixmap_menu_item_draw (GtkWidget
*widget
,
1620 GdkRectangle
*area
);
1621 static gint
gtk_pixmap_menu_item_expose (GtkWidget
*widget
,
1622 GdkEventExpose
*event
);
1624 /* we must override the following functions */
1626 static void gtk_pixmap_menu_item_map (GtkWidget
*widget
);
1627 static void gtk_pixmap_menu_item_size_allocate (GtkWidget
*widget
,
1628 GtkAllocation
*allocation
);
1629 static void gtk_pixmap_menu_item_forall (GtkContainer
*container
,
1630 gboolean include_internals
,
1631 GtkCallback callback
,
1632 gpointer callback_data
);
1633 static void gtk_pixmap_menu_item_size_request (GtkWidget
*widget
,
1634 GtkRequisition
*requisition
);
1635 static void gtk_pixmap_menu_item_remove (GtkContainer
*container
,
1638 static void changed_have_pixmap_status (GtkPixmapMenuItem
*menu_item
);
1640 static GtkMenuItemClass
*parent_class
= NULL
;
1644 #define BORDER_SPACING 3
1645 #define PMAP_WIDTH 20
1648 gtk_pixmap_menu_item_get_type (void)
1650 static GtkType pixmap_menu_item_type
= 0;
1652 if (!pixmap_menu_item_type
)
1654 GtkTypeInfo pixmap_menu_item_info
=
1656 (char *)"GtkPixmapMenuItem",
1657 sizeof (GtkPixmapMenuItem
),
1658 sizeof (GtkPixmapMenuItemClass
),
1659 (GtkClassInitFunc
) gtk_pixmap_menu_item_class_init
,
1660 (GtkObjectInitFunc
) gtk_pixmap_menu_item_init
,
1661 /* reserved_1 */ NULL
,
1662 /* reserved_2 */ NULL
,
1663 (GtkClassInitFunc
) NULL
,
1666 pixmap_menu_item_type
= gtk_type_unique (gtk_menu_item_get_type (),
1667 &pixmap_menu_item_info
);
1670 return pixmap_menu_item_type
;
1674 * gtk_pixmap_menu_item_new
1676 * Creates a new pixmap menu item. Use gtk_pixmap_menu_item_set_pixmap()
1677 * to set the pixmap wich is displayed at the left side.
1680 * &GtkWidget pointer to new menu item
1684 gtk_pixmap_menu_item_new (void)
1686 return GTK_WIDGET (gtk_type_new (gtk_pixmap_menu_item_get_type ()));
1690 gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass
*klass
)
1692 GtkObjectClass
*object_class
;
1693 GtkWidgetClass
*widget_class
;
1694 GtkMenuItemClass
*menu_item_class
;
1695 GtkContainerClass
*container_class
;
1697 object_class
= (GtkObjectClass
*) klass
;
1698 widget_class
= (GtkWidgetClass
*) klass
;
1699 menu_item_class
= (GtkMenuItemClass
*) klass
;
1700 container_class
= (GtkContainerClass
*) klass
;
1702 parent_class
= (GtkMenuItemClass
*) gtk_type_class (gtk_menu_item_get_type ());
1704 widget_class
->draw
= gtk_pixmap_menu_item_draw
;
1705 widget_class
->expose_event
= gtk_pixmap_menu_item_expose
;
1706 widget_class
->map
= gtk_pixmap_menu_item_map
;
1707 widget_class
->size_allocate
= gtk_pixmap_menu_item_size_allocate
;
1708 widget_class
->size_request
= gtk_pixmap_menu_item_size_request
;
1710 container_class
->forall
= gtk_pixmap_menu_item_forall
;
1711 container_class
->remove
= gtk_pixmap_menu_item_remove
;
1713 klass
->orig_toggle_size
= menu_item_class
->toggle_size
;
1714 klass
->have_pixmap_count
= 0;
1718 gtk_pixmap_menu_item_init (GtkPixmapMenuItem
*menu_item
)
1722 mi
= GTK_MENU_ITEM (menu_item
);
1724 menu_item
->pixmap
= NULL
;
1728 gtk_pixmap_menu_item_draw (GtkWidget
*widget
,
1731 g_return_if_fail (widget
!= NULL
);
1732 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
));
1733 g_return_if_fail (area
!= NULL
);
1735 if (GTK_WIDGET_CLASS (parent_class
)->draw
)
1736 (* GTK_WIDGET_CLASS (parent_class
)->draw
) (widget
, area
);
1738 if (GTK_WIDGET_DRAWABLE (widget
) &&
1739 GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
) {
1740 gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
),NULL
);
1745 gtk_pixmap_menu_item_expose (GtkWidget
*widget
,
1746 GdkEventExpose
*event
)
1748 g_return_val_if_fail (widget
!= NULL
, FALSE
);
1749 g_return_val_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
), FALSE
);
1750 g_return_val_if_fail (event
!= NULL
, FALSE
);
1752 if (GTK_WIDGET_CLASS (parent_class
)->expose_event
)
1753 (* GTK_WIDGET_CLASS (parent_class
)->expose_event
) (widget
, event
);
1755 if (GTK_WIDGET_DRAWABLE (widget
) &&
1756 GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
) {
1757 gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
),NULL
);
1764 * gtk_pixmap_menu_item_set_pixmap
1765 * @menu_item: Pointer to the pixmap menu item
1766 * @pixmap: Pointer to a pixmap widget
1768 * Set the pixmap of the menu item.
1773 gtk_pixmap_menu_item_set_pixmap (GtkPixmapMenuItem
*menu_item
,
1776 g_return_if_fail (menu_item
!= NULL
);
1777 g_return_if_fail (pixmap
!= NULL
);
1778 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (menu_item
));
1779 g_return_if_fail (GTK_IS_WIDGET (pixmap
));
1780 g_return_if_fail (menu_item
->pixmap
== NULL
);
1782 gtk_widget_set_parent (pixmap
, GTK_WIDGET (menu_item
));
1783 menu_item
->pixmap
= pixmap
;
1785 if (GTK_WIDGET_REALIZED (pixmap
->parent
) &&
1786 !GTK_WIDGET_REALIZED (pixmap
))
1787 gtk_widget_realize (pixmap
);
1789 if (GTK_WIDGET_VISIBLE (pixmap
->parent
)) {
1790 if (GTK_WIDGET_MAPPED (pixmap
->parent
) &&
1791 GTK_WIDGET_VISIBLE(pixmap
) &&
1792 !GTK_WIDGET_MAPPED (pixmap
))
1793 gtk_widget_map (pixmap
);
1796 changed_have_pixmap_status(menu_item
);
1798 if (GTK_WIDGET_VISIBLE (pixmap
) && GTK_WIDGET_VISIBLE (menu_item
))
1799 gtk_widget_queue_resize (pixmap
);
1803 gtk_pixmap_menu_item_map (GtkWidget
*widget
)
1805 GtkPixmapMenuItem
*menu_item
;
1807 g_return_if_fail (widget
!= NULL
);
1808 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
));
1810 menu_item
= GTK_PIXMAP_MENU_ITEM(widget
);
1812 GTK_WIDGET_CLASS(parent_class
)->map(widget
);
1814 if (menu_item
->pixmap
&&
1815 GTK_WIDGET_VISIBLE (menu_item
->pixmap
) &&
1816 !GTK_WIDGET_MAPPED (menu_item
->pixmap
))
1817 gtk_widget_map (menu_item
->pixmap
);
1821 gtk_pixmap_menu_item_size_allocate (GtkWidget
*widget
,
1822 GtkAllocation
*allocation
)
1824 GtkPixmapMenuItem
*pmenu_item
;
1826 pmenu_item
= GTK_PIXMAP_MENU_ITEM(widget
);
1828 if (pmenu_item
->pixmap
&& GTK_WIDGET_VISIBLE(pmenu_item
))
1830 GtkAllocation child_allocation
;
1833 border_width
= GTK_CONTAINER (widget
)->border_width
;
1835 child_allocation
.width
= pmenu_item
->pixmap
->requisition
.width
;
1836 child_allocation
.height
= pmenu_item
->pixmap
->requisition
.height
;
1837 child_allocation
.x
= border_width
+ BORDER_SPACING
;
1838 child_allocation
.y
= (border_width
+ BORDER_SPACING
1839 + (((allocation
->height
- child_allocation
.height
) - child_allocation
.x
)
1840 / 2)); /* center pixmaps vertically */
1841 gtk_widget_size_allocate (pmenu_item
->pixmap
, &child_allocation
);
1844 if (GTK_WIDGET_CLASS (parent_class
)->size_allocate
)
1845 GTK_WIDGET_CLASS(parent_class
)->size_allocate (widget
, allocation
);
1849 gtk_pixmap_menu_item_forall (GtkContainer
*container
,
1850 gboolean include_internals
,
1851 GtkCallback callback
,
1852 gpointer callback_data
)
1854 GtkPixmapMenuItem
*menu_item
;
1856 g_return_if_fail (container
!= NULL
);
1857 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container
));
1858 g_return_if_fail (callback
!= NULL
);
1860 menu_item
= GTK_PIXMAP_MENU_ITEM (container
);
1862 if (menu_item
->pixmap
)
1863 (* callback
) (menu_item
->pixmap
, callback_data
);
1865 GTK_CONTAINER_CLASS(parent_class
)->forall(container
,include_internals
,
1866 callback
,callback_data
);
1870 gtk_pixmap_menu_item_size_request (GtkWidget
*widget
,
1871 GtkRequisition
*requisition
)
1873 GtkPixmapMenuItem
*menu_item
;
1874 GtkRequisition req
= {0, 0};
1876 g_return_if_fail (widget
!= NULL
);
1877 g_return_if_fail (GTK_IS_MENU_ITEM (widget
));
1878 g_return_if_fail (requisition
!= NULL
);
1880 GTK_WIDGET_CLASS(parent_class
)->size_request(widget
,requisition
);
1882 menu_item
= GTK_PIXMAP_MENU_ITEM (widget
);
1884 if (menu_item
->pixmap
)
1885 gtk_widget_size_request(menu_item
->pixmap
, &req
);
1887 requisition
->height
= MAX(req
.height
+ GTK_CONTAINER(widget
)->border_width
+ BORDER_SPACING
, (unsigned int) requisition
->height
);
1888 requisition
->width
+= (req
.width
+ GTK_CONTAINER(widget
)->border_width
+ BORDER_SPACING
);
1892 gtk_pixmap_menu_item_remove (GtkContainer
*container
,
1896 gboolean widget_was_visible
;
1898 g_return_if_fail (container
!= NULL
);
1899 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container
));
1900 g_return_if_fail (child
!= NULL
);
1901 g_return_if_fail (GTK_IS_WIDGET (child
));
1903 bin
= GTK_BIN (container
);
1904 g_return_if_fail ((bin
->child
== child
||
1905 (GTK_PIXMAP_MENU_ITEM(container
)->pixmap
== child
)));
1907 widget_was_visible
= GTK_WIDGET_VISIBLE (child
);
1909 gtk_widget_unparent (child
);
1910 if (bin
->child
== child
)
1913 GTK_PIXMAP_MENU_ITEM(container
)->pixmap
= NULL
;
1914 changed_have_pixmap_status(GTK_PIXMAP_MENU_ITEM(container
));
1917 if (widget_was_visible
)
1918 gtk_widget_queue_resize (GTK_WIDGET (container
));
1922 /* important to only call this if there was actually a _change_ in pixmap == NULL */
1924 changed_have_pixmap_status (GtkPixmapMenuItem
*menu_item
)
1926 if (menu_item
->pixmap
!= NULL
) {
1927 GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
+= 1;
1929 if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
== 1) {
1930 /* Install pixmap toggle size */
1931 GTK_MENU_ITEM_GET_CLASS(menu_item
)->toggle_size
= MAX(GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->orig_toggle_size
, PMAP_WIDTH
);
1934 GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
-= 1;
1936 if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
== 0) {
1937 /* Install normal toggle size */
1938 GTK_MENU_ITEM_GET_CLASS(menu_item
)->toggle_size
= GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->orig_toggle_size
;
1942 /* Note that we actually need to do this for _all_ GtkPixmapMenuItem
1943 whenever the klass->toggle_size changes; but by doing it anytime
1944 this function is called, we get the same effect, just because of
1945 how the preferences option to show pixmaps works. Bogus, broken.
1947 if (GTK_WIDGET_VISIBLE(GTK_WIDGET(menu_item
)))
1948 gtk_widget_queue_resize(GTK_WIDGET(menu_item
));