]> git.saurik.com Git - wxWidgets.git/blame - src/gtk1/menu.cpp
fixed bug due to which all items in a virtual control stayed sometimes selected even...
[wxWidgets.git] / src / gtk1 / menu.cpp
CommitLineData
c801d85f
KB
1/////////////////////////////////////////////////////////////////////////////
2// Name: menu.cpp
3// Purpose:
4// Author: Robert Roebling
96fd301f 5// Id: $Id$
a81258be 6// Copyright: (c) 1998 Robert Roebling
96fd301f 7// Licence: wxWindows licence
c801d85f
KB
8/////////////////////////////////////////////////////////////////////////////
9
c801d85f
KB
10#ifdef __GNUG__
11#pragma implementation "menu.h"
6ca41e57 12#pragma implementation "menuitem.h"
c801d85f
KB
13#endif
14
96fd301f 15#include "wx/log.h"
30dea054 16#include "wx/intl.h"
06cfab17 17#include "wx/app.h"
37d403aa 18#include "wx/bitmap.h"
3dfac970 19#include "wx/menu.h"
c801d85f 20
974e8d94
VZ
21#if wxUSE_ACCEL
22 #include "wx/accel.h"
23#endif // wxUSE_ACCEL
24
9e691f46
VZ
25#include "wx/gtk/private.h"
26
cc47eed9 27#include <gdk/gdkkeysyms.h>
9e691f46
VZ
28
29// FIXME: is this right? somehow I don't think so (VZ)
30#ifdef __WXGTK20__
31 #include <glib-object.h>
32
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)
36
37 #define ACCEL_OBJECT GObject
38 #define ACCEL_OBJECTS(a) (a)->acceleratables
39 #define ACCEL_OBJ_CAST(obj) G_OBJECT(obj)
40#else // GTK+ 1.x
41 #define ACCEL_OBJECT GtkObject
42 #define ACCEL_OBJECTS(a) (a)->attach_objects
43 #define ACCEL_OBJ_CAST(obj) GTK_OBJECT(obj)
44#endif
83624f79 45
acfd422a
RR
46//-----------------------------------------------------------------------------
47// idle system
48//-----------------------------------------------------------------------------
49
50extern void wxapp_install_idle_handler();
51extern bool g_isIdle;
52
9e691f46
VZ
53#if GTK_CHECK_VERSION(1, 2, 0) && wxUSE_ACCEL
54 static wxString GetHotKey( const wxMenuItem& item );
717a57c2
VZ
55#endif
56
cc47eed9
RR
57//-----------------------------------------------------------------------------
58// substitute for missing GtkPixmapMenuItem
59//-----------------------------------------------------------------------------
37d403aa 60
9e691f46
VZ
61// FIXME: I can't make this compile with GTK+ 2.0, disabling for now (VZ)
62#ifndef __WXGTK20__
63 #define USE_MENU_BITMAPS
64#endif
65
66#ifdef USE_MENU_BITMAPS
67
cc47eed9
RR
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))
37d403aa 70#define GTK_PIXMAP_MENU_ITEM_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_PIXMAP_MENU_ITEM, GtkPixmapMenuItemClass))
cc47eed9 71#define GTK_IS_PIXMAP_MENU_ITEM(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_PIXMAP_MENU_ITEM))
37d403aa
JS
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))
cc47eed9 74#define GTK_PIXMAP_MENU_ITEM_GET_CLASS(obj) (GTK_PIXMAP_MENU_ITEM_CLASS( GTK_OBJECT_GET_CLASS(obj)))
37d403aa
JS
75
76#ifndef GTK_MENU_ITEM_GET_CLASS
77#define GTK_MENU_ITEM_GET_CLASS(obj) (GTK_MENU_ITEM_CLASS( GTK_OBJECT_GET_CLASS(obj)))
78#endif
79
80typedef struct _GtkPixmapMenuItem GtkPixmapMenuItem;
81typedef struct _GtkPixmapMenuItemClass GtkPixmapMenuItemClass;
82
83struct _GtkPixmapMenuItem
84{
cc47eed9 85 GtkMenuItem menu_item;
37d403aa 86
cc47eed9 87 GtkWidget *pixmap;
37d403aa
JS
88};
89
90struct _GtkPixmapMenuItemClass
91{
cc47eed9 92 GtkMenuItemClass parent_class;
37d403aa 93
cc47eed9
RR
94 guint orig_toggle_size;
95 guint have_pixmap_count;
37d403aa
JS
96};
97
98
cc47eed9
RR
99GtkType gtk_pixmap_menu_item_get_type (void);
100GtkWidget* gtk_pixmap_menu_item_new (void);
101void gtk_pixmap_menu_item_set_pixmap (GtkPixmapMenuItem *menu_item,
102 GtkWidget *pixmap);
37d403aa 103
9e691f46
VZ
104#endif // USE_MENU_BITMAPS
105
f6bcfd97
BP
106//-----------------------------------------------------------------------------
107// idle system
108//-----------------------------------------------------------------------------
109
110static wxString wxReplaceUnderscore( const wxString& title )
111{
112 const wxChar *pc;
2368dcda 113
f6bcfd97
BP
114 /* GTK 1.2 wants to have "_" instead of "&" for accelerators */
115 wxString str;
116 for ( pc = title; *pc != wxT('\0'); pc++ )
117 {
118 if (*pc == wxT('&'))
119 {
4e9cbd33 120#if GTK_CHECK_VERSION(1, 2, 0)
f6bcfd97 121 str << wxT('_');
4e9cbd33 122#endif
f6bcfd97 123 }
4e9cbd33
VS
124#if GTK_CHECK_VERSION(2, 0, 0)
125 else if (*pc == wxT('/'))
126 {
127 str << wxT("\\/");
128 }
129 else if (*pc == wxT('\\'))
130 {
131 str << wxT("\\\\");
132 }
133#elif GTK_CHECK_VERSION(1, 2, 0)
f6bcfd97
BP
134 else if (*pc == wxT('/'))
135 {
136 str << wxT('\\');
f6bcfd97 137 }
4e9cbd33 138#endif
f6bcfd97
BP
139 else
140 {
141#if __WXGTK12__
142 if ( *pc == wxT('_') )
143 {
144 // underscores must be doubled to prevent them from being
145 // interpreted as accelerator character prefix by GTK
146 str << *pc;
147 }
148#endif // GTK+ 1.2
149
150 str << *pc;
151 }
152 }
153 return str;
154}
155
c801d85f
KB
156//-----------------------------------------------------------------------------
157// wxMenuBar
158//-----------------------------------------------------------------------------
159
160IMPLEMENT_DYNAMIC_CLASS(wxMenuBar,wxWindow)
161
3502e687
RR
162wxMenuBar::wxMenuBar( long style )
163{
1e133b7d 164 /* the parent window is known after wxFrame::SetMenu() */
23280650 165 m_needParent = FALSE;
ae53c98c 166 m_style = style;
9c884972 167 m_invokingWindow = (wxWindow*) NULL;
23280650 168
4dcaf11a 169 if (!PreCreation( (wxWindow*) NULL, wxDefaultPosition, wxDefaultSize ) ||
223d09f6 170 !CreateBase( (wxWindow*) NULL, -1, wxDefaultPosition, wxDefaultSize, style, wxDefaultValidator, wxT("menubar") ))
4dcaf11a 171 {
223d09f6 172 wxFAIL_MSG( wxT("wxMenuBar creation failed") );
455fadaa 173 return;
4dcaf11a 174 }
3502e687
RR
175
176 m_menus.DeleteContents( TRUE );
177
1e133b7d 178 /* GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has. */
9e691f46 179#if GTK_CHECK_VERSION(1, 2, 1)
1e133b7d
RR
180 m_accel = gtk_accel_group_new();
181 m_factory = gtk_item_factory_new( GTK_TYPE_MENU_BAR, "<main>", m_accel );
182 m_menubar = gtk_item_factory_get_widget( m_factory, "<main>" );
23280650 183#else
3502e687 184 m_menubar = gtk_menu_bar_new();
1e133b7d 185#endif
3502e687
RR
186
187 if (style & wxMB_DOCKABLE)
188 {
189 m_widget = gtk_handle_box_new();
c626a8b7
VZ
190 gtk_container_add( GTK_CONTAINER(m_widget), GTK_WIDGET(m_menubar) );
191 gtk_widget_show( GTK_WIDGET(m_menubar) );
3502e687
RR
192 }
193 else
194 {
195 m_widget = GTK_WIDGET(m_menubar);
196 }
197
198 PostCreation();
c4608a8a 199
db434467 200 ApplyWidgetStyle();
3502e687
RR
201}
202
96fd301f 203wxMenuBar::wxMenuBar()
c801d85f 204{
1e133b7d
RR
205 /* the parent window is known after wxFrame::SetMenu() */
206 m_needParent = FALSE;
ae53c98c 207 m_style = 0;
9c884972 208 m_invokingWindow = (wxWindow*) NULL;
23280650 209
4dcaf11a 210 if (!PreCreation( (wxWindow*) NULL, wxDefaultPosition, wxDefaultSize ) ||
223d09f6 211 !CreateBase( (wxWindow*) NULL, -1, wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, wxT("menubar") ))
4dcaf11a 212 {
223d09f6 213 wxFAIL_MSG( wxT("wxMenuBar creation failed") );
455fadaa 214 return;
4dcaf11a 215 }
974e8d94 216
83624f79 217 m_menus.DeleteContents( TRUE );
96fd301f 218
1e133b7d 219 /* GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has. */
9e691f46 220#if GTK_CHECK_VERSION(1, 2, 1)
1e133b7d
RR
221 m_accel = gtk_accel_group_new();
222 m_factory = gtk_item_factory_new( GTK_TYPE_MENU_BAR, "<main>", m_accel );
223 m_menubar = gtk_item_factory_get_widget( m_factory, "<main>" );
23280650 224#else
83624f79 225 m_menubar = gtk_menu_bar_new();
1e133b7d 226#endif
8bbe427f 227
828f655f 228 m_widget = GTK_WIDGET(m_menubar);
96fd301f 229
83624f79 230 PostCreation();
c4608a8a 231
db434467 232 ApplyWidgetStyle();
6de97a3b 233}
c801d85f 234
1e133b7d
RR
235wxMenuBar::~wxMenuBar()
236{
d1b15f03 237// gtk_object_unref( GTK_OBJECT(m_factory) ); why not ?
1e133b7d
RR
238}
239
5bd9e519
RR
240static void wxMenubarUnsetInvokingWindow( wxMenu *menu, wxWindow *win )
241{
242 menu->SetInvokingWindow( (wxWindow*) NULL );
243
9e691f46 244#if GTK_CHECK_VERSION(1, 2, 0)
5bd9e519 245 wxWindow *top_frame = win;
8487f887 246 while (top_frame->GetParent() && !(top_frame->IsTopLevel()))
bd77da97 247 top_frame = top_frame->GetParent();
5bd9e519
RR
248
249 /* support for native hot keys */
9e691f46 250 gtk_accel_group_detach( menu->m_accel, ACCEL_OBJ_CAST(top_frame->m_widget) );
5bd9e519
RR
251#endif
252
1987af7e 253 wxMenuItemList::Node *node = menu->GetMenuItems().GetFirst();
5bd9e519
RR
254 while (node)
255 {
1987af7e 256 wxMenuItem *menuitem = node->GetData();
5bd9e519
RR
257 if (menuitem->IsSubMenu())
258 wxMenubarUnsetInvokingWindow( menuitem->GetSubMenu(), win );
1987af7e 259 node = node->GetNext();
5bd9e519
RR
260 }
261}
262
263static void wxMenubarSetInvokingWindow( wxMenu *menu, wxWindow *win )
264{
265 menu->SetInvokingWindow( win );
266
9e691f46 267#if GTK_CHECK_VERSION(1, 2, 1)
5bd9e519 268 wxWindow *top_frame = win;
8487f887 269 while (top_frame->GetParent() && !(top_frame->IsTopLevel()))
bd77da97 270 top_frame = top_frame->GetParent();
5bd9e519
RR
271
272 /* support for native hot keys */
9e691f46
VZ
273 ACCEL_OBJECT *obj = ACCEL_OBJ_CAST(top_frame->m_widget);
274 if ( !g_slist_find( ACCEL_OBJECTS(menu->m_accel), obj ) )
b4da05a6 275 gtk_accel_group_attach( menu->m_accel, obj );
9e691f46 276#endif // GTK+ 1.2.1+
5bd9e519 277
1987af7e 278 wxMenuItemList::Node *node = menu->GetMenuItems().GetFirst();
5bd9e519
RR
279 while (node)
280 {
1987af7e 281 wxMenuItem *menuitem = node->GetData();
5bd9e519
RR
282 if (menuitem->IsSubMenu())
283 wxMenubarSetInvokingWindow( menuitem->GetSubMenu(), win );
1987af7e 284 node = node->GetNext();
5bd9e519
RR
285 }
286}
287
288void wxMenuBar::SetInvokingWindow( wxWindow *win )
289{
9c884972 290 m_invokingWindow = win;
9e691f46 291#if GTK_CHECK_VERSION(1, 2, 1)
5bd9e519 292 wxWindow *top_frame = win;
8487f887 293 while (top_frame->GetParent() && !(top_frame->IsTopLevel()))
bd77da97 294 top_frame = top_frame->GetParent();
5bd9e519
RR
295
296 /* support for native key accelerators indicated by underscroes */
9e691f46
VZ
297 ACCEL_OBJECT *obj = ACCEL_OBJ_CAST(top_frame->m_widget);
298 if ( !g_slist_find( ACCEL_OBJECTS(m_accel), obj ) )
b4da05a6 299 gtk_accel_group_attach( m_accel, obj );
9e691f46 300#endif // GTK+ 1.2.1+
5bd9e519 301
1987af7e 302 wxMenuList::Node *node = m_menus.GetFirst();
5bd9e519
RR
303 while (node)
304 {
1987af7e 305 wxMenu *menu = node->GetData();
5bd9e519 306 wxMenubarSetInvokingWindow( menu, win );
1987af7e 307 node = node->GetNext();
5bd9e519
RR
308 }
309}
310
311void wxMenuBar::UnsetInvokingWindow( wxWindow *win )
312{
9c884972 313 m_invokingWindow = (wxWindow*) NULL;
9e691f46 314#if GTK_CHECK_VERSION(1, 2, 1)
5bd9e519 315 wxWindow *top_frame = win;
8487f887 316 while (top_frame->GetParent() && !(top_frame->IsTopLevel()))
bd77da97 317 top_frame = top_frame->GetParent();
5bd9e519
RR
318
319 /* support for native key accelerators indicated by underscroes */
9e691f46
VZ
320 gtk_accel_group_detach( m_accel, ACCEL_OBJ_CAST(top_frame->m_widget) );
321#endif // GTK+ 1.2.1+
5bd9e519 322
1987af7e 323 wxMenuList::Node *node = m_menus.GetFirst();
5bd9e519
RR
324 while (node)
325 {
1987af7e 326 wxMenu *menu = node->GetData();
5bd9e519 327 wxMenubarUnsetInvokingWindow( menu, win );
1987af7e 328 node = node->GetNext();
5bd9e519
RR
329 }
330}
331
3dfac970 332bool wxMenuBar::Append( wxMenu *menu, const wxString &title )
c801d85f 333{
f03ec224
VZ
334 if ( !wxMenuBarBase::Append( menu, title ) )
335 return FALSE;
336
337 return GtkAppend(menu, title);
338}
23280650 339
f03ec224
VZ
340bool wxMenuBar::GtkAppend(wxMenu *menu, const wxString& title)
341{
f6bcfd97 342 wxString str( wxReplaceUnderscore( title ) );
1e133b7d
RR
343
344 /* this doesn't have much effect right now */
345 menu->SetTitle( str );
23280650 346
1e133b7d 347 /* GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has. */
9e691f46 348#if GTK_CHECK_VERSION(1, 2, 1)
1e133b7d
RR
349
350 /* local buffer in multibyte form */
2b2edbed 351 wxString buf;
223d09f6 352 buf << wxT('/') << str.c_str();
c980c992 353
dc6c62a9 354 char *cbuf = new char[buf.Length()+1];
c980c992
GL
355 strcpy(cbuf, buf.mbc_str());
356
1e133b7d 357 GtkItemFactoryEntry entry;
c980c992 358 entry.path = (gchar *)cbuf; // const_cast
1e133b7d
RR
359 entry.accelerator = (gchar*) NULL;
360 entry.callback = (GtkItemFactoryCallback) NULL;
361 entry.callback_action = 0;
90350682 362 entry.item_type = (char *)"<Branch>";
2b2edbed 363
1e133b7d 364 gtk_item_factory_create_item( m_factory, &entry, (gpointer) this, 2 ); /* what is 2 ? */
1e133b7d 365 /* in order to get the pointer to the item we need the item text _without_ underscores */
223d09f6 366 wxString tmp = wxT("<main>/");
f6bcfd97 367 const wxChar *pc;
223d09f6 368 for ( pc = str; *pc != wxT('\0'); pc++ )
1e133b7d 369 {
455fadaa
VZ
370 // contrary to the common sense, we must throw out _all_ underscores,
371 // (i.e. "Hello__World" => "HelloWorld" and not "Hello_World" as we
974e8d94 372 // might naively think). IMHO it's a bug in GTK+ (VZ)
223d09f6 373 while (*pc == wxT('_'))
455fadaa 374 pc++;
2b2edbed 375 tmp << *pc;
034be888 376 }
1e133b7d 377 menu->m_owner = gtk_item_factory_get_item( m_factory, tmp.mb_str() );
1e133b7d 378 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu->m_owner), menu->m_menu );
2b2edbed 379 delete [] cbuf;
1e133b7d 380#else
96fd301f 381
1e133b7d 382 menu->m_owner = gtk_menu_item_new_with_label( str.mb_str() );
2b1c162e
RR
383 gtk_widget_show( menu->m_owner );
384 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu->m_owner), menu->m_menu );
96fd301f 385
2b1c162e 386 gtk_menu_bar_append( GTK_MENU_BAR(m_menubar), menu->m_owner );
23280650 387
1e133b7d 388#endif
9c884972
RR
389
390 // m_invokingWindow is set after wxFrame::SetMenuBar(). This call enables
391 // adding menu later on.
392 if (m_invokingWindow)
393 wxMenubarSetInvokingWindow( menu, m_invokingWindow );
3dfac970
VZ
394
395 return TRUE;
396}
397
398bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title)
399{
400 if ( !wxMenuBarBase::Insert(pos, menu, title) )
401 return FALSE;
402
f03ec224
VZ
403#if __WXGTK12__
404 // GTK+ doesn't have a function to insert a menu using GtkItemFactory (as
405 // of version 1.2.6), so we first append the item and then change its
406 // index
407 if ( !GtkAppend(menu, title) )
408 return FALSE;
409
186baeb2
RR
410 if (pos+1 >= m_menus.GetCount())
411 return TRUE;
412
f03ec224
VZ
413 GtkMenuShell *menu_shell = GTK_MENU_SHELL(m_factory->widget);
414 gpointer data = g_list_last(menu_shell->children)->data;
415 menu_shell->children = g_list_remove(menu_shell->children, data);
416 menu_shell->children = g_list_insert(menu_shell->children, data, pos);
417
418 return TRUE;
419#else // GTK < 1.2
420 // this should be easy to do with GTK 1.0 - can use standard functions for
421 // this and don't need any hacks like above, but as I don't have GTK 1.0
422 // any more I can't do it
423 wxFAIL_MSG( wxT("TODO") );
3dfac970
VZ
424
425 return FALSE;
f03ec224 426#endif // GTK 1.2/1.0
3dfac970
VZ
427}
428
429wxMenu *wxMenuBar::Replace(size_t pos, wxMenu *menu, const wxString& title)
430{
f03ec224
VZ
431 // remove the old item and insert a new one
432 wxMenu *menuOld = Remove(pos);
433 if ( menuOld && !Insert(pos, menu, title) )
434 {
1d62a8b4 435 return (wxMenu*) NULL;
f03ec224 436 }
3dfac970 437
f03ec224
VZ
438 // either Insert() succeeded or Remove() failed and menuOld is NULL
439 return menuOld;
3dfac970
VZ
440}
441
442wxMenu *wxMenuBar::Remove(size_t pos)
443{
f03ec224
VZ
444 wxMenu *menu = wxMenuBarBase::Remove(pos);
445 if ( !menu )
1d62a8b4 446 return (wxMenu*) NULL;
f03ec224 447
589c9fff 448/*
c4608a8a 449 GtkMenuShell *menu_shell = GTK_MENU_SHELL(m_factory->widget);
589c9fff 450
1d62a8b4
RR
451 printf( "factory entries before %d\n", (int)g_slist_length(m_factory->items) );
452 printf( "menu shell entries before %d\n", (int)g_list_length( menu_shell->children ) );
589c9fff 453*/
c4608a8a 454
1d62a8b4
RR
455 // unparent calls unref() and that would delete the widget so we raise
456 // the ref count to 2 artificially before invoking unparent.
457 gtk_widget_ref( menu->m_menu );
458 gtk_widget_unparent( menu->m_menu );
c4608a8a 459
1d62a8b4 460 gtk_widget_destroy( menu->m_owner );
c4608a8a 461
589c9fff 462/*
1d62a8b4
RR
463 printf( "factory entries after %d\n", (int)g_slist_length(m_factory->items) );
464 printf( "menu shell entries after %d\n", (int)g_list_length( menu_shell->children ) );
589c9fff 465*/
c4608a8a 466
1d62a8b4 467 return menu;
6de97a3b 468}
96fd301f 469
716b7364 470static int FindMenuItemRecursive( const wxMenu *menu, const wxString &menuString, const wxString &itemString )
c801d85f 471{
f6bcfd97 472 if (wxMenuItem::GetLabelFromText(menu->GetTitle()) == wxMenuItem::GetLabelFromText(menuString))
83624f79
RR
473 {
474 int res = menu->FindItem( itemString );
c626a8b7
VZ
475 if (res != wxNOT_FOUND)
476 return res;
83624f79 477 }
c626a8b7 478
1987af7e 479 wxMenuItemList::Node *node = menu->GetMenuItems().GetFirst();
83624f79
RR
480 while (node)
481 {
1987af7e 482 wxMenuItem *item = node->GetData();
83624f79
RR
483 if (item->IsSubMenu())
484 return FindMenuItemRecursive(item->GetSubMenu(), menuString, itemString);
2b1c162e 485
1987af7e 486 node = node->GetNext();
83624f79
RR
487 }
488
c626a8b7
VZ
489 return wxNOT_FOUND;
490}
491
c801d85f
KB
492int wxMenuBar::FindMenuItem( const wxString &menuString, const wxString &itemString ) const
493{
1987af7e 494 wxMenuList::Node *node = m_menus.GetFirst();
83624f79
RR
495 while (node)
496 {
1987af7e 497 wxMenu *menu = node->GetData();
83624f79 498 int res = FindMenuItemRecursive( menu, menuString, itemString);
1987af7e
VZ
499 if (res != -1)
500 return res;
501 node = node->GetNext();
83624f79 502 }
1987af7e
VZ
503
504 return wxNOT_FOUND;
6de97a3b 505}
c801d85f 506
c626a8b7 507// Find a wxMenuItem using its id. Recurses down into sub-menus
96fd301f 508static wxMenuItem* FindMenuItemByIdRecursive(const wxMenu* menu, int id)
716b7364 509{
717a57c2 510 wxMenuItem* result = menu->FindChildItem(id);
716b7364 511
1987af7e 512 wxMenuItemList::Node *node = menu->GetMenuItems().GetFirst();
c626a8b7 513 while ( node && result == NULL )
83624f79 514 {
1987af7e 515 wxMenuItem *item = node->GetData();
83624f79 516 if (item->IsSubMenu())
c626a8b7 517 {
83624f79 518 result = FindMenuItemByIdRecursive( item->GetSubMenu(), id );
c626a8b7 519 }
1987af7e 520 node = node->GetNext();
83624f79 521 }
96fd301f 522
83624f79 523 return result;
6de97a3b 524}
716b7364 525
3dfac970 526wxMenuItem* wxMenuBar::FindItem( int id, wxMenu **menuForItem ) const
716b7364 527{
83624f79 528 wxMenuItem* result = 0;
1987af7e 529 wxMenuList::Node *node = m_menus.GetFirst();
83624f79
RR
530 while (node && result == 0)
531 {
1987af7e 532 wxMenu *menu = node->GetData();
83624f79 533 result = FindMenuItemByIdRecursive( menu, id );
1987af7e 534 node = node->GetNext();
83624f79 535 }
c626a8b7 536
3dfac970
VZ
537 if ( menuForItem )
538 {
539 *menuForItem = result ? result->GetMenu() : (wxMenu *)NULL;
540 }
c626a8b7 541
3dfac970 542 return result;
bbe0af5b
RR
543}
544
3dfac970 545void wxMenuBar::EnableTop( size_t pos, bool flag )
bbe0af5b 546{
3dfac970 547 wxMenuList::Node *node = m_menus.Item( pos );
c626a8b7 548
223d09f6 549 wxCHECK_RET( node, wxT("menu not found") );
c626a8b7 550
3dfac970 551 wxMenu* menu = node->GetData();
c626a8b7
VZ
552
553 if (menu->m_owner)
554 gtk_widget_set_sensitive( menu->m_owner, flag );
bbe0af5b
RR
555}
556
3dfac970 557wxString wxMenuBar::GetLabelTop( size_t pos ) const
bbe0af5b 558{
3dfac970 559 wxMenuList::Node *node = m_menus.Item( pos );
c626a8b7 560
223d09f6 561 wxCHECK_MSG( node, wxT("invalid"), wxT("menu not found") );
c626a8b7 562
3dfac970 563 wxMenu* menu = node->GetData();
c626a8b7 564
f6bcfd97
BP
565 wxString label;
566 wxString text( menu->GetTitle() );
9e691f46 567#if GTK_CHECK_VERSION(1, 2, 0)
f6bcfd97
BP
568 for ( const wxChar *pc = text.c_str(); *pc; pc++ )
569 {
570 if ( *pc == wxT('_') || *pc == wxT('&') )
571 {
572 // '_' is the escape character for GTK+ and '&' is the one for
573 // wxWindows - skip both of them
574 continue;
575 }
576
577 label += *pc;
578 }
579#else // GTK+ 1.0
580 label = text;
581#endif // GTK+ 1.2/1.0
582
583 return label;
bbe0af5b
RR
584}
585
3dfac970 586void wxMenuBar::SetLabelTop( size_t pos, const wxString& label )
bbe0af5b 587{
3dfac970 588 wxMenuList::Node *node = m_menus.Item( pos );
c626a8b7 589
223d09f6 590 wxCHECK_RET( node, wxT("menu not found") );
c626a8b7 591
3dfac970 592 wxMenu* menu = node->GetData();
c626a8b7 593
f6bcfd97
BP
594 wxString str( wxReplaceUnderscore( label ) );
595
596 menu->SetTitle( str );
597
598 if (menu->m_owner)
599 {
600 GtkLabel *label = GTK_LABEL( GTK_BIN(menu->m_owner)->child );
601
602 /* set new text */
603 gtk_label_set( label, str.mb_str());
604
605 /* reparse key accel */
606 (void)gtk_label_parse_uline (GTK_LABEL(label), str.mb_str() );
607 gtk_accel_label_refetch( GTK_ACCEL_LABEL(label) );
608 }
609
bbe0af5b
RR
610}
611
c801d85f 612//-----------------------------------------------------------------------------
cf7a7e13 613// "activate"
c801d85f
KB
614//-----------------------------------------------------------------------------
615
6de97a3b 616static void gtk_menu_clicked_callback( GtkWidget *widget, wxMenu *menu )
c801d85f 617{
1e6feb95
VZ
618 if (g_isIdle)
619 wxapp_install_idle_handler();
c4608a8a 620
83624f79 621 int id = menu->FindMenuIdByMenuItem(widget);
96fd301f 622
83624f79 623 /* should find it for normal (not popup) menu */
1e6feb95
VZ
624 wxASSERT_MSG( (id != -1) || (menu->GetInvokingWindow() != NULL),
625 _T("menu item not found in gtk_menu_clicked_callback") );
96fd301f 626
c626a8b7
VZ
627 if (!menu->IsEnabled(id))
628 return;
96fd301f 629
717a57c2 630 wxMenuItem* item = menu->FindChildItem( id );
223d09f6 631 wxCHECK_RET( item, wxT("error in menu item callback") );
c626a8b7
VZ
632
633 if (item->IsCheckable())
2d17d68f 634 {
705dd80a
VZ
635 bool isReallyChecked = item->IsChecked(),
636 isInternallyChecked = item->wxMenuItemBase::IsChecked();
637
638 // ensure that the internal state is always consistent with what is
639 // shown on the screen
640 item->wxMenuItemBase::Check(isReallyChecked);
641
642 // we must not report the events for the radio button going up nor the
643 // events resulting from the calls to wxMenuItem::Check()
644 if ( (item->GetKind() == wxITEM_RADIO && !isReallyChecked) ||
645 (isInternallyChecked == isReallyChecked) )
2d17d68f 646 {
2d17d68f 647 return;
c626a8b7 648 }
705dd80a
VZ
649
650 // the user pressed on the menu item: report the event below
2d17d68f
RR
651 }
652
705dd80a 653 menu->SendEvent(id, item->IsCheckable() ? item->IsChecked() : -1);
cf7a7e13
RR
654}
655
656//-----------------------------------------------------------------------------
657// "select"
658//-----------------------------------------------------------------------------
659
660static void gtk_menu_hilight_callback( GtkWidget *widget, wxMenu *menu )
661{
acfd422a
RR
662 if (g_isIdle) wxapp_install_idle_handler();
663
83624f79
RR
664 int id = menu->FindMenuIdByMenuItem(widget);
665
666 wxASSERT( id != -1 ); // should find it!
cf7a7e13 667
c626a8b7
VZ
668 if (!menu->IsEnabled(id))
669 return;
cf7a7e13 670
342b6a2f 671 wxMenuEvent event( wxEVT_MENU_HIGHLIGHT, id );
83624f79 672 event.SetEventObject( menu );
cf7a7e13 673
c626a8b7
VZ
674 if (menu->GetEventHandler()->ProcessEvent(event))
675 return;
6de97a3b 676
83624f79
RR
677 wxWindow *win = menu->GetInvokingWindow();
678 if (win) win->GetEventHandler()->ProcessEvent( event );
6de97a3b 679}
c801d85f 680
cd743a6f
RR
681//-----------------------------------------------------------------------------
682// "deselect"
683//-----------------------------------------------------------------------------
684
685static void gtk_menu_nolight_callback( GtkWidget *widget, wxMenu *menu )
686{
acfd422a
RR
687 if (g_isIdle) wxapp_install_idle_handler();
688
cd743a6f
RR
689 int id = menu->FindMenuIdByMenuItem(widget);
690
691 wxASSERT( id != -1 ); // should find it!
692
c626a8b7
VZ
693 if (!menu->IsEnabled(id))
694 return;
cd743a6f
RR
695
696 wxMenuEvent event( wxEVT_MENU_HIGHLIGHT, -1 );
697 event.SetEventObject( menu );
698
c626a8b7
VZ
699 if (menu->GetEventHandler()->ProcessEvent(event))
700 return;
cd743a6f
RR
701
702 wxWindow *win = menu->GetInvokingWindow();
c626a8b7
VZ
703 if (win)
704 win->GetEventHandler()->ProcessEvent( event );
cd743a6f
RR
705}
706
cf7a7e13 707//-----------------------------------------------------------------------------
db1b4961 708// wxMenuItem
cf7a7e13
RR
709//-----------------------------------------------------------------------------
710
7dbf5360 711IMPLEMENT_DYNAMIC_CLASS(wxMenuItem, wxObject)
974e8d94
VZ
712
713wxMenuItem *wxMenuItemBase::New(wxMenu *parentMenu,
714 int id,
715 const wxString& name,
716 const wxString& help,
d65c269b 717 wxItemKind kind,
974e8d94
VZ
718 wxMenu *subMenu)
719{
d65c269b 720 return new wxMenuItem(parentMenu, id, name, help, kind, subMenu);
974e8d94 721}
96fd301f 722
974e8d94
VZ
723wxMenuItem::wxMenuItem(wxMenu *parentMenu,
724 int id,
725 const wxString& text,
726 const wxString& help,
d65c269b 727 wxItemKind kind,
974e8d94 728 wxMenu *subMenu)
d65c269b 729 : wxMenuItemBase(parentMenu, id, text, help, kind, subMenu)
2368dcda 730{
092f7536 731 Init(text);
2368dcda
VZ
732}
733
734wxMenuItem::wxMenuItem(wxMenu *parentMenu,
735 int id,
736 const wxString& text,
737 const wxString& help,
738 bool isCheckable,
739 wxMenu *subMenu)
740 : wxMenuItemBase(parentMenu, id, text, help,
741 isCheckable ? wxITEM_CHECK : wxITEM_NORMAL, subMenu)
742{
092f7536 743 Init(text);
2368dcda
VZ
744}
745
092f7536 746void wxMenuItem::Init(const wxString& text)
c801d85f 747{
37d403aa 748 m_labelWidget = (GtkWidget *) NULL;
83624f79 749 m_menuItem = (GtkWidget *) NULL;
974e8d94 750
092f7536 751 DoSetText(text);
6de97a3b 752}
c801d85f 753
d1b15f03
RR
754wxMenuItem::~wxMenuItem()
755{
756 // don't delete menu items, the menus take care of that
757}
758
717a57c2 759// return the menu item text without any menu accels
3b59cdbf
VZ
760/* static */
761wxString wxMenuItemBase::GetLabelFromText(const wxString& text)
717a57c2
VZ
762{
763 wxString label;
2368dcda 764
3b59cdbf 765 for ( const wxChar *pc = text.c_str(); *pc; pc++ )
717a57c2 766 {
d76419bd 767 if ( *pc == wxT('_') )
717a57c2 768 {
d76419bd
RR
769 // wxGTK escapes "xxx_xxx" to "xxx__xxx"
770 pc++;
771 label += *pc;
772 continue;
773 }
2368dcda 774
d76419bd
RR
775 if ( *pc == wxT('&') )
776 {
777 // wxMSW escapes &
717a57c2
VZ
778 continue;
779 }
780
781 label += *pc;
782 }
717a57c2
VZ
783
784 return label;
785}
786
787void wxMenuItem::SetText( const wxString& str )
788{
5869f93f
JS
789 // Some optimization to avoid flicker
790 wxString oldLabel = m_text;
791 oldLabel = wxStripMenuCodes(oldLabel.BeforeFirst('\t'));
792 oldLabel.Replace(wxT("_"), wxT(""));
793 wxString label1 = wxStripMenuCodes(str.BeforeFirst('\t'));
794 if (oldLabel == label1)
795 return;
796
717a57c2 797 DoSetText(str);
354aa1e3
RR
798
799 if (m_menuItem)
800 {
37d403aa
JS
801 GtkLabel *label;
802 if (m_labelWidget)
803 label = (GtkLabel*) m_labelWidget;
804 else
805 label = GTK_LABEL( GTK_BIN(m_menuItem)->child );
717a57c2
VZ
806
807 /* set new text */
354aa1e3 808 gtk_label_set( label, m_text.mb_str());
717a57c2
VZ
809
810 /* reparse key accel */
1987af7e 811 (void)gtk_label_parse_uline (GTK_LABEL(label), m_text.mb_str() );
354aa1e3
RR
812 gtk_accel_label_refetch( GTK_ACCEL_LABEL(label) );
813 }
814}
815
c626a8b7 816// it's valid for this function to be called even if m_menuItem == NULL
974e8d94 817void wxMenuItem::DoSetText( const wxString& str )
716b7364 818{
ab46dc18 819 /* '\t' is the deliminator indicating a hot key */
974e8d94 820 m_text.Empty();
ab46dc18 821 const wxChar *pc = str;
223d09f6 822 for (; (*pc != wxT('\0')) && (*pc != wxT('\t')); pc++ )
83624f79 823 {
4e9cbd33 824#if GTK_CHECK_VERSION(1, 2, 0)
223d09f6 825 if (*pc == wxT('&'))
23280650 826 {
223d09f6 827 m_text << wxT('_');
572d7461 828 }
223d09f6 829 else if ( *pc == wxT('_') ) // escape underscores
572d7461 830 {
223d09f6 831 m_text << wxT("__");
572d7461 832 }
4e9cbd33
VS
833#else // GTK+ < 1.2.0
834 if (*pc == wxT('&'))
835 {
836 }
837#endif
838#if GTK_CHECK_VERSION(2, 0, 0)
839 else if (*pc == wxT('/')) // we have to escape slashes
840 {
841 m_text << wxT("\\/");
842 }
843 else if (*pc == wxT('\\')) // we have to double backslashes
844 {
845 m_text << wxT("\\\\");
846 }
847#elif GTK_CHECK_VERSION(1, 2, 0)
223d09f6 848 else if (*pc == wxT('/')) /* we have to filter out slashes ... */
23280650 849 {
223d09f6 850 m_text << wxT('\\'); /* ... and replace them with back slashes */
034be888 851 }
4e9cbd33 852#endif
d38ceae8 853 else
354aa1e3 854 m_text << *pc;
83624f79 855 }
23280650 856
837904f2 857 /* only GTK 1.2 knows about hot keys */
223d09f6 858 m_hotKey = wxT("");
9e691f46
VZ
859
860#if GTK_CHECK_VERSION(1, 2, 0)
223d09f6 861 if(*pc == wxT('\t'))
d7dbc98a
KB
862 {
863 pc++;
864 m_hotKey = pc;
865 }
9e691f46 866#endif // GTK+ 1.2.0+
716b7364
RR
867}
868
717a57c2
VZ
869#if wxUSE_ACCEL
870
871wxAcceleratorEntry *wxMenuItem::GetAccel() const
872{
1987af7e 873 if ( !GetHotKey() )
717a57c2
VZ
874 {
875 // nothing
876 return (wxAcceleratorEntry *)NULL;
877 }
878
879 // as wxGetAccelFromString() looks for TAB, insert a dummy one here
880 wxString label;
1987af7e 881 label << wxT('\t') << GetHotKey();
717a57c2
VZ
882
883 return wxGetAccelFromString(label);
884}
885
886#endif // wxUSE_ACCEL
887
96fd301f 888void wxMenuItem::Check( bool check )
716b7364 889{
223d09f6 890 wxCHECK_RET( m_menuItem, wxT("invalid menu item") );
db1b4961 891
974e8d94
VZ
892 if (check == m_isChecked)
893 return;
2d17d68f 894
974e8d94 895 wxMenuItemBase::Check( check );
0472ece7 896
24bcaec3 897 switch ( GetKind() )
0472ece7 898 {
24bcaec3
VZ
899 case wxITEM_CHECK:
900 case wxITEM_RADIO:
901 gtk_check_menu_item_set_state( (GtkCheckMenuItem*)m_menuItem, (gint)check );
902 break;
903
904 default:
905 wxFAIL_MSG( _T("can't check this item") );
0472ece7 906 }
716b7364
RR
907}
908
8bbe427f
VZ
909void wxMenuItem::Enable( bool enable )
910{
223d09f6 911 wxCHECK_RET( m_menuItem, wxT("invalid menu item") );
db1b4961 912
83624f79 913 gtk_widget_set_sensitive( m_menuItem, enable );
974e8d94 914 wxMenuItemBase::Enable( enable );
a9c96bcc
RR
915}
916
96fd301f 917bool wxMenuItem::IsChecked() const
716b7364 918{
223d09f6 919 wxCHECK_MSG( m_menuItem, FALSE, wxT("invalid menu item") );
db1b4961 920
974e8d94
VZ
921 wxCHECK_MSG( IsCheckable(), FALSE,
922 wxT("can't get state of uncheckable item!") );
96fd301f 923
974e8d94 924 return ((GtkCheckMenuItem*)m_menuItem)->active != 0;
716b7364
RR
925}
926
354aa1e3
RR
927wxString wxMenuItem::GetFactoryPath() const
928{
d76419bd
RR
929 /* in order to get the pointer to the item we need the item text
930 _without_ underscores */
354aa1e3 931 wxString path( wxT("<main>/") );
717a57c2 932
d76419bd
RR
933 for ( const wxChar *pc = m_text.c_str(); *pc; pc++ )
934 {
935 if ( *pc == wxT('_') || *pc == wxT('&') )
936 {
937 // remove '_' and '&' unconditionally
938 continue;
939 }
2368dcda 940
d76419bd
RR
941 path += *pc;
942 }
2368dcda 943
354aa1e3
RR
944 return path;
945}
946
db1b4961 947//-----------------------------------------------------------------------------
83624f79 948// wxMenu
db1b4961
RR
949//-----------------------------------------------------------------------------
950
c801d85f
KB
951IMPLEMENT_DYNAMIC_CLASS(wxMenu,wxEvtHandler)
952
717a57c2 953void wxMenu::Init()
c801d85f 954{
9e691f46 955#if GTK_CHECK_VERSION(1, 2, 0)
034be888
RR
956 m_accel = gtk_accel_group_new();
957 m_factory = gtk_item_factory_new( GTK_TYPE_MENU, "<main>", m_accel );
958 m_menu = gtk_item_factory_get_widget( m_factory, "<main>" );
23280650 959#else
83624f79 960 m_menu = gtk_menu_new(); // Do not show!
034be888 961#endif
8bbe427f 962
2b1c162e 963 m_owner = (GtkWidget*) NULL;
2b2edbed 964
9e691f46 965#if GTK_CHECK_VERSION(1, 2, 0)
2b2edbed
KB
966 /* Tearoffs are entries, just like separators. So if we want this
967 menu to be a tear-off one, we just append a tearoff entry
968 immediately. */
969 if(m_style & wxMENU_TEAROFF)
970 {
971 GtkItemFactoryEntry entry;
90350682 972 entry.path = (char *)"/tearoff";
2b2edbed
KB
973 entry.callback = (GtkItemFactoryCallback) NULL;
974 entry.callback_action = 0;
90350682 975 entry.item_type = (char *)"<Tearoff>";
2b2edbed
KB
976 entry.accelerator = (gchar*) NULL;
977 gtk_item_factory_create_item( m_factory, &entry, (gpointer) this, 2 ); /* what is 2 ? */
23280650 978 //GtkWidget *menuItem = gtk_item_factory_get_widget( m_factory, "<main>/tearoff" );
2b2edbed 979 }
9e691f46 980#endif // GTK+ 1.2.0+
c801d85f 981
717a57c2
VZ
982 // append the title as the very first entry if we have it
983 if ( !!m_title )
d1b15f03 984 {
717a57c2
VZ
985 Append(-2, m_title);
986 AppendSeparator();
d1b15f03 987 }
717a57c2 988}
15a2076a 989
717a57c2
VZ
990wxMenu::~wxMenu()
991{
a583e623 992 m_items.Clear();
f03ec224 993
d1b15f03 994 gtk_widget_destroy( m_menu );
974e8d94 995
d1b15f03 996 gtk_object_unref( GTK_OBJECT(m_factory) );
c2dd8380
GL
997}
998
32db328c 999bool wxMenu::GtkAppend(wxMenuItem *mitem)
c2dd8380 1000{
717a57c2 1001 GtkWidget *menuItem;
96fd301f 1002
9e691f46 1003#if defined(USE_MENU_BITMAPS) || !GTK_CHECK_VERSION(1, 2, 0)
37d403aa 1004 bool appended = FALSE;
9e691f46 1005#endif
37d403aa 1006
d65c269b 1007#if GTK_CHECK_VERSION(1, 2, 0)
24bcaec3
VZ
1008 // does this item terminate the current radio group?
1009 bool endOfRadioGroup = TRUE;
d65c269b
VZ
1010#endif // GTK+ >= 1.2
1011
717a57c2
VZ
1012 if ( mitem->IsSeparator() )
1013 {
9e691f46 1014#if GTK_CHECK_VERSION(1, 2, 0)
717a57c2 1015 GtkItemFactoryEntry entry;
90350682 1016 entry.path = (char *)"/sep";
717a57c2
VZ
1017 entry.callback = (GtkItemFactoryCallback) NULL;
1018 entry.callback_action = 0;
90350682 1019 entry.item_type = (char *)"<Separator>";
717a57c2
VZ
1020 entry.accelerator = (gchar*) NULL;
1021
1022 gtk_item_factory_create_item( m_factory, &entry, (gpointer) this, 2 ); /* what is 2 ? */
1023
1024 /* this will be wrong for more than one separator. do we care? */
1025 menuItem = gtk_item_factory_get_widget( m_factory, "<main>/sep" );
24bcaec3
VZ
1026
1027 // we might have a separator inside a radio group
1028 endOfRadioGroup = FALSE;
717a57c2
VZ
1029#else // GTK+ 1.0
1030 menuItem = gtk_menu_item_new();
1031#endif // GTK 1.2/1.0
1032 }
1033 else if ( mitem->IsSubMenu() )
837904f2 1034 {
9e691f46 1035#if GTK_CHECK_VERSION(1, 2, 0)
717a57c2
VZ
1036 /* text has "_" instead of "&" after mitem->SetText() */
1037 wxString text( mitem->GetText() );
974e8d94 1038
717a57c2
VZ
1039 /* local buffer in multibyte form */
1040 char buf[200];
1041 strcpy( buf, "/" );
1042 strcat( buf, text.mb_str() );
50592885 1043
717a57c2
VZ
1044 GtkItemFactoryEntry entry;
1045 entry.path = buf;
1046 entry.callback = (GtkItemFactoryCallback) 0;
1047 entry.callback_action = 0;
90350682 1048 entry.item_type = (char *)"<Branch>";
a583e623 1049 entry.accelerator = (gchar*) NULL;
50592885 1050
717a57c2 1051 gtk_item_factory_create_item( m_factory, &entry, (gpointer) this, 2 ); /* what is 2 ? */
50592885 1052
717a57c2 1053 wxString path( mitem->GetFactoryPath() );
1987af7e 1054 menuItem = gtk_item_factory_get_item( m_factory, path.mb_str() );
717a57c2 1055#else // GTK+ 1.0
1987af7e 1056 menuItem = gtk_menu_item_new_with_label(mitem->GetText().mbc_str());
717a57c2 1057#endif // GTK 1.2/1.0
50592885 1058
1987af7e 1059 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menuItem), mitem->GetSubMenu()->m_menu );
e7200790
VZ
1060
1061 // if adding a submenu to a menu already existing in the menu bar, we
1062 // must set invoking window to allow processing events from this
1063 // submenu
1064 if ( m_invokingWindow )
1065 wxMenubarSetInvokingWindow(mitem->GetSubMenu(), m_invokingWindow);
837904f2 1066 }
9e691f46 1067#ifdef USE_MENU_BITMAPS
37d403aa
JS
1068 else if (mitem->GetBitmap().Ok()) // An item with bitmap
1069 {
cc47eed9
RR
1070 wxString text( mitem->GetText() );
1071 const wxBitmap *bitmap = &mitem->GetBitmap();
2368dcda 1072
cc47eed9
RR
1073 menuItem = gtk_pixmap_menu_item_new ();
1074 GtkWidget *label = gtk_accel_label_new (text.mb_str());
1075 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
1076 gtk_container_add (GTK_CONTAINER (menuItem), label);
1077 guint accel_key = gtk_label_parse_uline (GTK_LABEL(label), text.mb_str() );
1078 gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (label), menuItem);
1079 if (accel_key != GDK_VoidSymbol)
1080 {
1081 gtk_widget_add_accelerator (menuItem,
1082 "activate_item",
1083 gtk_menu_ensure_uline_accel_group (GTK_MENU (m_menu)),
1084 accel_key, 0,
1085 GTK_ACCEL_LOCKED);
1086 }
1087 gtk_widget_show (label);
37d403aa 1088
cc47eed9 1089 mitem->SetLabelWidget(label);
37d403aa 1090
cc47eed9
RR
1091 GtkWidget* pixmap = gtk_pixmap_new( bitmap->GetPixmap(), bitmap->GetMask() ? bitmap->GetMask()->GetBitmap() : (GdkBitmap* )NULL);
1092 gtk_widget_show(pixmap);
1093 gtk_pixmap_menu_item_set_pixmap(GTK_PIXMAP_MENU_ITEM( menuItem ), pixmap);
37d403aa
JS
1094
1095 gtk_signal_connect( GTK_OBJECT(menuItem), "activate",
1096 GTK_SIGNAL_FUNC(gtk_menu_clicked_callback),
1097 (gpointer)this );
1098 gtk_menu_append( GTK_MENU(m_menu), menuItem );
1099 gtk_widget_show( menuItem );
1100
37d403aa
JS
1101 appended = TRUE; // We've done this, don't do it again
1102 }
9e691f46 1103#endif // USE_MENU_BITMAPS
717a57c2
VZ
1104 else // a normal item
1105 {
9e691f46 1106#if GTK_CHECK_VERSION(1, 2, 0)
717a57c2
VZ
1107 /* text has "_" instead of "&" after mitem->SetText() */
1108 wxString text( mitem->GetText() );
1109
1110 /* local buffer in multibyte form */
1111 char buf[200];
1112 strcpy( buf, "/" );
d65c269b
VZ
1113 strncat( buf, text.mb_str(), WXSIZEOF(buf) - 2 );
1114 buf[WXSIZEOF(buf) - 1] = '\0';
717a57c2
VZ
1115
1116 GtkItemFactoryEntry entry;
1117 entry.path = buf;
1118 entry.callback = (GtkItemFactoryCallback) gtk_menu_clicked_callback;
1119 entry.callback_action = 0;
d65c269b
VZ
1120
1121 wxString pathRadio;
1122 const char *item_type;
1123 switch ( mitem->GetKind() )
1124 {
546bfbea 1125 case wxITEM_CHECK:
d65c269b
VZ
1126 item_type = "<CheckItem>";
1127 break;
1128
546bfbea 1129 case wxITEM_RADIO:
d65c269b
VZ
1130 if ( m_pathLastRadio.empty() )
1131 {
1132 // start of a new radio group
1133 item_type = "<RadioItem>";
1134 m_pathLastRadio = buf + 1;
1135 }
1136 else // continue the radio group
1137 {
1138 pathRadio = m_pathLastRadio;
1139 pathRadio.Replace("_", "");
1140 pathRadio.Prepend("<main>/");
1141 item_type = pathRadio;
1142 }
1143
24bcaec3
VZ
1144 // continue the existing radio group, if any
1145 endOfRadioGroup = FALSE;
d65c269b
VZ
1146 break;
1147
1148 default:
1149 wxFAIL_MSG( _T("unexpected menu item kind") );
1150 // fall through
1151
546bfbea 1152 case wxITEM_NORMAL:
d65c269b
VZ
1153 item_type = "<Item>";
1154 break;
1155 }
1156
1157 entry.item_type = (char *)item_type; // cast needed for GTK+
a583e623 1158 entry.accelerator = (gchar*) NULL;
23280650 1159
974e8d94 1160#if wxUSE_ACCEL
717a57c2
VZ
1161 // due to an apparent bug in GTK+, we have to use a static buffer here -
1162 // otherwise GTK+ 1.2.2 manages to override the memory we pass to it
1163 // somehow! (VZ)
3ca6a5f0
BP
1164 static char s_accel[50]; // must be big enougg
1165 wxString tmp( GetHotKey(*mitem) );
1166 strncpy(s_accel, tmp.mb_str(), WXSIZEOF(s_accel));
717a57c2
VZ
1167 entry.accelerator = s_accel;
1168#else // !wxUSE_ACCEL
1169 entry.accelerator = (char*) NULL;
1170#endif // wxUSE_ACCEL/!wxUSE_ACCEL
96fd301f 1171
717a57c2 1172 gtk_item_factory_create_item( m_factory, &entry, (gpointer) this, 2 ); /* what is 2 ? */
cf7a7e13 1173
717a57c2 1174 wxString path( mitem->GetFactoryPath() );
1987af7e 1175 menuItem = gtk_item_factory_get_widget( m_factory, path.mb_str() );
717a57c2 1176#else // GTK+ 1.0
1987af7e
VZ
1177 menuItem = checkable ? gtk_check_menu_item_new_with_label( mitem->GetText().mb_str() )
1178 : gtk_menu_item_new_with_label( mitem->GetText().mb_str() );
034be888 1179
717a57c2
VZ
1180 gtk_signal_connect( GTK_OBJECT(menuItem), "activate",
1181 GTK_SIGNAL_FUNC(gtk_menu_clicked_callback),
1182 (gpointer)this );
1183#endif // GTK+ 1.2/1.0
1184 }
23280650 1185
717a57c2
VZ
1186 if ( !mitem->IsSeparator() )
1187 {
1188 gtk_signal_connect( GTK_OBJECT(menuItem), "select",
1189 GTK_SIGNAL_FUNC(gtk_menu_hilight_callback),
1190 (gpointer)this );
837904f2 1191
717a57c2
VZ
1192 gtk_signal_connect( GTK_OBJECT(menuItem), "deselect",
1193 GTK_SIGNAL_FUNC(gtk_menu_nolight_callback),
1194 (gpointer)this );
1195 }
23280650 1196
9e691f46 1197#if !GTK_CHECK_VERSION(1, 2, 0)
37d403aa
JS
1198 if (!appended)
1199 {
1200 gtk_menu_append( GTK_MENU(m_menu), menuItem );
1201 gtk_widget_show( menuItem );
1202 }
717a57c2 1203#endif // GTK+ 1.0
23280650 1204
837904f2 1205 mitem->SetMenuItem(menuItem);
837904f2 1206
d65c269b 1207#if GTK_CHECK_VERSION(1, 2, 0)
24bcaec3 1208 if ( endOfRadioGroup )
d65c269b
VZ
1209 {
1210 m_pathLastRadio.clear();
1211 }
1212#endif // GTK+ >= 1.2
1213
32db328c 1214 return TRUE;
6de97a3b 1215}
c801d85f 1216
32db328c 1217bool wxMenu::DoAppend(wxMenuItem *mitem)
828f655f 1218{
32db328c 1219 return GtkAppend(mitem) && wxMenuBase::DoAppend(mitem);
c33c4050
RR
1220}
1221
717a57c2 1222bool wxMenu::DoInsert(size_t pos, wxMenuItem *item)
c33c4050 1223{
717a57c2
VZ
1224 if ( !wxMenuBase::DoInsert(pos, item) )
1225 return FALSE;
c626a8b7 1226
32db328c
VZ
1227#ifdef __WXGTK12__
1228 // GTK+ doesn't have a function to insert a menu using GtkItemFactory (as
1229 // of version 1.2.6), so we first append the item and then change its
1230 // index
1231 if ( !GtkAppend(item) )
1232 return FALSE;
1233
f6bcfd97
BP
1234 if ( m_style & wxMENU_TEAROFF )
1235 {
1236 // change the position as the first item is the tear-off marker
1237 pos++;
1238 }
1239
32db328c
VZ
1240 GtkMenuShell *menu_shell = GTK_MENU_SHELL(m_factory->widget);
1241 gpointer data = g_list_last(menu_shell->children)->data;
1242 menu_shell->children = g_list_remove(menu_shell->children, data);
1243 menu_shell->children = g_list_insert(menu_shell->children, data, pos);
1244
1245 return TRUE;
1246#else // GTK < 1.2
1247 // this should be easy to do...
1248 wxFAIL_MSG( wxT("not implemented") );
c626a8b7 1249
717a57c2 1250 return FALSE;
96fa7876 1251#endif // GTK 1.2/1.0
c33c4050
RR
1252}
1253
717a57c2 1254wxMenuItem *wxMenu::DoRemove(wxMenuItem *item)
c33c4050 1255{
717a57c2
VZ
1256 if ( !wxMenuBase::DoRemove(item) )
1257 return (wxMenuItem *)NULL;
c626a8b7 1258
717a57c2
VZ
1259 // TODO: this code doesn't delete the item factory item and this seems
1260 // impossible as of GTK 1.2.6.
1261 gtk_widget_destroy( item->GetMenuItem() );
c626a8b7 1262
717a57c2 1263 return item;
c33c4050
RR
1264}
1265
96fd301f
VZ
1266int wxMenu::FindMenuIdByMenuItem( GtkWidget *menuItem ) const
1267{
83624f79
RR
1268 wxNode *node = m_items.First();
1269 while (node)
1270 {
1271 wxMenuItem *item = (wxMenuItem*)node->Data();
1272 if (item->GetMenuItem() == menuItem)
1273 return item->GetId();
1274 node = node->Next();
1275 }
96fd301f 1276
c626a8b7 1277 return wxNOT_FOUND;
6de97a3b 1278}
c801d85f 1279
717a57c2
VZ
1280// ----------------------------------------------------------------------------
1281// helpers
1282// ----------------------------------------------------------------------------
1283
9e691f46 1284#if GTK_CHECK_VERSION(1, 2, 0) && wxUSE_ACCEL
a070d8ce 1285
717a57c2 1286static wxString GetHotKey( const wxMenuItem& item )
c801d85f 1287{
717a57c2
VZ
1288 wxString hotkey;
1289
1290 wxAcceleratorEntry *accel = item.GetAccel();
1291 if ( accel )
83624f79 1292 {
717a57c2
VZ
1293 int flags = accel->GetFlags();
1294 if ( flags & wxACCEL_ALT )
1295 hotkey += wxT("<alt>");
1296 if ( flags & wxACCEL_CTRL )
1297 hotkey += wxT("<control>");
1298 if ( flags & wxACCEL_SHIFT )
1299 hotkey += wxT("<shift>");
1300
1301 int code = accel->GetKeyCode();
1302 switch ( code )
c626a8b7 1303 {
717a57c2
VZ
1304 case WXK_F1:
1305 case WXK_F2:
1306 case WXK_F3:
1307 case WXK_F4:
1308 case WXK_F5:
1309 case WXK_F6:
1310 case WXK_F7:
1311 case WXK_F8:
1312 case WXK_F9:
1313 case WXK_F10:
1314 case WXK_F11:
1315 case WXK_F12:
1316 hotkey << wxT('F') << code - WXK_F1 + 1;
1317 break;
2368dcda 1318
a070d8ce
VZ
1319 // TODO: we should use gdk_keyval_name() (a.k.a.
1320 // XKeysymToString) here as well as hardcoding the keysym
1321 // names this might be not portable
3ca6a5f0
BP
1322 case WXK_NUMPAD_INSERT:
1323 hotkey << wxT("KP_Insert" );
1324 break;
1325 case WXK_NUMPAD_DELETE:
1326 hotkey << wxT("KP_Delete" );
1327 break;
1328 case WXK_INSERT:
1329 hotkey << wxT("Insert" );
1330 break;
1331 case WXK_DELETE:
1332 hotkey << wxT("Delete" );
1333 break;
96fd301f 1334
a070d8ce
VZ
1335 // if there are any other keys wxGetAccelFromString() may
1336 // return, we should process them here
8bbe427f 1337
717a57c2 1338 default:
a070d8ce 1339 if ( code < 127 )
717a57c2 1340 {
a070d8ce
VZ
1341 gchar *name = gdk_keyval_name((guint)code);
1342 if ( name )
1343 {
1344 hotkey << name;
1345 break;
1346 }
717a57c2 1347 }
c801d85f 1348
717a57c2
VZ
1349 wxFAIL_MSG( wxT("unknown keyboard accel") );
1350 }
c801d85f 1351
717a57c2 1352 delete accel;
631f1bfe 1353 }
717a57c2
VZ
1354
1355 return hotkey;
631f1bfe 1356}
a070d8ce 1357
717a57c2 1358#endif // wxUSE_ACCEL
631f1bfe 1359
37d403aa 1360
cc47eed9
RR
1361//-----------------------------------------------------------------------------
1362// substitute for missing GtkPixmapMenuItem
1363//-----------------------------------------------------------------------------
37d403aa 1364
9e691f46
VZ
1365#ifdef USE_MENU_BITMAPS
1366
37d403aa
JS
1367/*
1368 * Copyright (C) 1998, 1999, 2000 Free Software Foundation
1369 * All rights reserved.
1370 *
1371 * This file is part of the Gnome Library.
1372 *
1373 * The Gnome Library is free software; you can redistribute it and/or
1374 * modify it under the terms of the GNU Library General Public License as
1375 * published by the Free Software Foundation; either version 2 of the
1376 * License, or (at your option) any later version.
1377 *
1378 * The Gnome Library is distributed in the hope that it will be useful,
1379 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1380 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1381 * Library General Public License for more details.
1382 *
1383 * You should have received a copy of the GNU Library General Public
1384 * License along with the Gnome Library; see the file COPYING.LIB. If not,
1385 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
1386 * Boston, MA 02111-1307, USA.
1387 */
1388/*
1389 @NOTATION@
1390 */
1391
1392/* Author: Dietmar Maurer <dm@vlsivie.tuwien.ac.at> */
1393
37d403aa
JS
1394#include <gtk/gtkaccellabel.h>
1395#include <gtk/gtksignal.h>
1396#include <gtk/gtkmenuitem.h>
1397#include <gtk/gtkmenu.h>
1398#include <gtk/gtkcontainer.h>
1399
90350682
VZ
1400extern "C"
1401{
1402
37d403aa
JS
1403static void gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass *klass);
1404static void gtk_pixmap_menu_item_init (GtkPixmapMenuItem *menu_item);
1405static void gtk_pixmap_menu_item_draw (GtkWidget *widget,
1406 GdkRectangle *area);
1407static gint gtk_pixmap_menu_item_expose (GtkWidget *widget,
1408 GdkEventExpose *event);
1409
1410/* we must override the following functions */
1411
1412static void gtk_pixmap_menu_item_map (GtkWidget *widget);
1413static void gtk_pixmap_menu_item_size_allocate (GtkWidget *widget,
1414 GtkAllocation *allocation);
1415static void gtk_pixmap_menu_item_forall (GtkContainer *container,
1416 gboolean include_internals,
1417 GtkCallback callback,
1418 gpointer callback_data);
1419static void gtk_pixmap_menu_item_size_request (GtkWidget *widget,
1420 GtkRequisition *requisition);
1421static void gtk_pixmap_menu_item_remove (GtkContainer *container,
1422 GtkWidget *child);
1423
1424static void changed_have_pixmap_status (GtkPixmapMenuItem *menu_item);
1425
1426static GtkMenuItemClass *parent_class = NULL;
1427
90350682
VZ
1428}
1429
37d403aa
JS
1430#define BORDER_SPACING 3
1431#define PMAP_WIDTH 20
1432
1433GtkType
1434gtk_pixmap_menu_item_get_type (void)
1435{
1436 static GtkType pixmap_menu_item_type = 0;
1437
1438 if (!pixmap_menu_item_type)
1439 {
1440 GtkTypeInfo pixmap_menu_item_info =
1441 {
90350682 1442 (char *)"GtkPixmapMenuItem",
37d403aa
JS
1443 sizeof (GtkPixmapMenuItem),
1444 sizeof (GtkPixmapMenuItemClass),
1445 (GtkClassInitFunc) gtk_pixmap_menu_item_class_init,
1446 (GtkObjectInitFunc) gtk_pixmap_menu_item_init,
1447 /* reserved_1 */ NULL,
1448 /* reserved_2 */ NULL,
1449 (GtkClassInitFunc) NULL,
1450 };
1451
2368dcda 1452 pixmap_menu_item_type = gtk_type_unique (gtk_menu_item_get_type (),
37d403aa
JS
1453 &pixmap_menu_item_info);
1454 }
1455
1456 return pixmap_menu_item_type;
1457}
1458
1459/**
1460 * gtk_pixmap_menu_item_new
1461 *
2368dcda 1462 * Creates a new pixmap menu item. Use gtk_pixmap_menu_item_set_pixmap()
37d403aa
JS
1463 * to set the pixmap wich is displayed at the left side.
1464 *
1465 * Returns:
1466 * &GtkWidget pointer to new menu item
1467 **/
1468
1469GtkWidget*
1470gtk_pixmap_menu_item_new (void)
1471{
1472 return GTK_WIDGET (gtk_type_new (gtk_pixmap_menu_item_get_type ()));
1473}
1474
1475static void
1476gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass *klass)
1477{
1478 GtkObjectClass *object_class;
1479 GtkWidgetClass *widget_class;
1480 GtkMenuItemClass *menu_item_class;
1481 GtkContainerClass *container_class;
1482
1483 object_class = (GtkObjectClass*) klass;
1484 widget_class = (GtkWidgetClass*) klass;
1485 menu_item_class = (GtkMenuItemClass*) klass;
1486 container_class = (GtkContainerClass*) klass;
1487
1488 parent_class = (GtkMenuItemClass*) gtk_type_class (gtk_menu_item_get_type ());
1489
1490 widget_class->draw = gtk_pixmap_menu_item_draw;
1491 widget_class->expose_event = gtk_pixmap_menu_item_expose;
1492 widget_class->map = gtk_pixmap_menu_item_map;
1493 widget_class->size_allocate = gtk_pixmap_menu_item_size_allocate;
1494 widget_class->size_request = gtk_pixmap_menu_item_size_request;
1495
1496 container_class->forall = gtk_pixmap_menu_item_forall;
1497 container_class->remove = gtk_pixmap_menu_item_remove;
1498
1499 klass->orig_toggle_size = menu_item_class->toggle_size;
1500 klass->have_pixmap_count = 0;
1501}
1502
1503static void
1504gtk_pixmap_menu_item_init (GtkPixmapMenuItem *menu_item)
1505{
1506 GtkMenuItem *mi;
1507
1508 mi = GTK_MENU_ITEM (menu_item);
1509
1510 menu_item->pixmap = NULL;
1511}
1512
1513static void
1514gtk_pixmap_menu_item_draw (GtkWidget *widget,
1515 GdkRectangle *area)
1516{
1517 g_return_if_fail (widget != NULL);
1518 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget));
1519 g_return_if_fail (area != NULL);
1520
1521 if (GTK_WIDGET_CLASS (parent_class)->draw)
1522 (* GTK_WIDGET_CLASS (parent_class)->draw) (widget, area);
1523
2368dcda 1524 if (GTK_WIDGET_DRAWABLE (widget) &&
37d403aa
JS
1525 GTK_PIXMAP_MENU_ITEM(widget)->pixmap) {
1526 gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget)->pixmap),NULL);
1527 }
1528}
1529
1530static gint
1531gtk_pixmap_menu_item_expose (GtkWidget *widget,
1532 GdkEventExpose *event)
1533{
1534 g_return_val_if_fail (widget != NULL, FALSE);
1535 g_return_val_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget), FALSE);
1536 g_return_val_if_fail (event != NULL, FALSE);
1537
1538 if (GTK_WIDGET_CLASS (parent_class)->expose_event)
1539 (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
1540
2368dcda 1541 if (GTK_WIDGET_DRAWABLE (widget) &&
37d403aa
JS
1542 GTK_PIXMAP_MENU_ITEM(widget)->pixmap) {
1543 gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget)->pixmap),NULL);
1544 }
1545
1546 return FALSE;
1547}
1548
1549/**
1550 * gtk_pixmap_menu_item_set_pixmap
1551 * @menu_item: Pointer to the pixmap menu item
1552 * @pixmap: Pointer to a pixmap widget
1553 *
1554 * Set the pixmap of the menu item.
1555 *
1556 **/
1557
1558void
1559gtk_pixmap_menu_item_set_pixmap (GtkPixmapMenuItem *menu_item,
1560 GtkWidget *pixmap)
1561{
1562 g_return_if_fail (menu_item != NULL);
1563 g_return_if_fail (pixmap != NULL);
1564 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (menu_item));
1565 g_return_if_fail (GTK_IS_WIDGET (pixmap));
1566 g_return_if_fail (menu_item->pixmap == NULL);
1567
1568 gtk_widget_set_parent (pixmap, GTK_WIDGET (menu_item));
1569 menu_item->pixmap = pixmap;
1570
1571 if (GTK_WIDGET_REALIZED (pixmap->parent) &&
1572 !GTK_WIDGET_REALIZED (pixmap))
1573 gtk_widget_realize (pixmap);
2368dcda
VZ
1574
1575 if (GTK_WIDGET_VISIBLE (pixmap->parent)) {
37d403aa
JS
1576 if (GTK_WIDGET_MAPPED (pixmap->parent) &&
1577 GTK_WIDGET_VISIBLE(pixmap) &&
1578 !GTK_WIDGET_MAPPED (pixmap))
1579 gtk_widget_map (pixmap);
1580 }
1581
1582 changed_have_pixmap_status(menu_item);
2368dcda 1583
37d403aa
JS
1584 if (GTK_WIDGET_VISIBLE (pixmap) && GTK_WIDGET_VISIBLE (menu_item))
1585 gtk_widget_queue_resize (pixmap);
1586}
1587
1588static void
1589gtk_pixmap_menu_item_map (GtkWidget *widget)
1590{
1591 GtkPixmapMenuItem *menu_item;
1592
1593 g_return_if_fail (widget != NULL);
1594 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget));
1595
1596 menu_item = GTK_PIXMAP_MENU_ITEM(widget);
1597
1598 GTK_WIDGET_CLASS(parent_class)->map(widget);
1599
1600 if (menu_item->pixmap &&
1601 GTK_WIDGET_VISIBLE (menu_item->pixmap) &&
1602 !GTK_WIDGET_MAPPED (menu_item->pixmap))
1603 gtk_widget_map (menu_item->pixmap);
1604}
1605
1606static void
1607gtk_pixmap_menu_item_size_allocate (GtkWidget *widget,
1608 GtkAllocation *allocation)
1609{
1610 GtkPixmapMenuItem *pmenu_item;
1611
1612 pmenu_item = GTK_PIXMAP_MENU_ITEM(widget);
1613
1614 if (pmenu_item->pixmap && GTK_WIDGET_VISIBLE(pmenu_item))
1615 {
1616 GtkAllocation child_allocation;
1617 int border_width;
1618
1619 border_width = GTK_CONTAINER (widget)->border_width;
1620
1621 child_allocation.width = pmenu_item->pixmap->requisition.width;
1622 child_allocation.height = pmenu_item->pixmap->requisition.height;
1623 child_allocation.x = border_width + BORDER_SPACING;
1624 child_allocation.y = (border_width + BORDER_SPACING
1625 + (((allocation->height - child_allocation.height) - child_allocation.x)
1626 / 2)); /* center pixmaps vertically */
1627 gtk_widget_size_allocate (pmenu_item->pixmap, &child_allocation);
1628 }
1629
1630 if (GTK_WIDGET_CLASS (parent_class)->size_allocate)
1631 GTK_WIDGET_CLASS(parent_class)->size_allocate (widget, allocation);
1632}
1633
1634static void
1635gtk_pixmap_menu_item_forall (GtkContainer *container,
1636 gboolean include_internals,
1637 GtkCallback callback,
1638 gpointer callback_data)
1639{
1640 GtkPixmapMenuItem *menu_item;
1641
1642 g_return_if_fail (container != NULL);
1643 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container));
1644 g_return_if_fail (callback != NULL);
1645
1646 menu_item = GTK_PIXMAP_MENU_ITEM (container);
1647
1648 if (menu_item->pixmap)
1649 (* callback) (menu_item->pixmap, callback_data);
1650
1651 GTK_CONTAINER_CLASS(parent_class)->forall(container,include_internals,
1652 callback,callback_data);
1653}
1654
1655static void
1656gtk_pixmap_menu_item_size_request (GtkWidget *widget,
1657 GtkRequisition *requisition)
1658{
1659 GtkPixmapMenuItem *menu_item;
1660 GtkRequisition req = {0, 0};
1661
1662 g_return_if_fail (widget != NULL);
1663 g_return_if_fail (GTK_IS_MENU_ITEM (widget));
1664 g_return_if_fail (requisition != NULL);
1665
1666 GTK_WIDGET_CLASS(parent_class)->size_request(widget,requisition);
1667
1668 menu_item = GTK_PIXMAP_MENU_ITEM (widget);
2368dcda 1669
37d403aa
JS
1670 if (menu_item->pixmap)
1671 gtk_widget_size_request(menu_item->pixmap, &req);
1672
1673 requisition->height = MAX(req.height + GTK_CONTAINER(widget)->border_width + BORDER_SPACING, (unsigned int) requisition->height);
1674 requisition->width += (req.width + GTK_CONTAINER(widget)->border_width + BORDER_SPACING);
1675}
1676
1677static void
1678gtk_pixmap_menu_item_remove (GtkContainer *container,
1679 GtkWidget *child)
1680{
1681 GtkBin *bin;
1682 gboolean widget_was_visible;
1683
1684 g_return_if_fail (container != NULL);
1685 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container));
1686 g_return_if_fail (child != NULL);
1687 g_return_if_fail (GTK_IS_WIDGET (child));
1688
1689 bin = GTK_BIN (container);
2368dcda 1690 g_return_if_fail ((bin->child == child ||
37d403aa
JS
1691 (GTK_PIXMAP_MENU_ITEM(container)->pixmap == child)));
1692
1693 widget_was_visible = GTK_WIDGET_VISIBLE (child);
2368dcda 1694
37d403aa
JS
1695 gtk_widget_unparent (child);
1696 if (bin->child == child)
2368dcda 1697 bin->child = NULL;
37d403aa
JS
1698 else {
1699 GTK_PIXMAP_MENU_ITEM(container)->pixmap = NULL;
1700 changed_have_pixmap_status(GTK_PIXMAP_MENU_ITEM(container));
1701 }
2368dcda 1702
37d403aa
JS
1703 if (widget_was_visible)
1704 gtk_widget_queue_resize (GTK_WIDGET (container));
1705}
1706
1707
1708/* important to only call this if there was actually a _change_ in pixmap == NULL */
1709static void
1710changed_have_pixmap_status (GtkPixmapMenuItem *menu_item)
1711{
1712 if (menu_item->pixmap != NULL) {
1713 GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item)->have_pixmap_count += 1;
1714
1715 if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item)->have_pixmap_count == 1) {
1716 /* Install pixmap toggle size */
1717 GTK_MENU_ITEM_GET_CLASS(menu_item)->toggle_size = MAX(GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item)->orig_toggle_size, PMAP_WIDTH);
1718 }
1719 } else {
1720 GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item)->have_pixmap_count -= 1;
1721
1722 if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item)->have_pixmap_count == 0) {
1723 /* Install normal toggle size */
2368dcda 1724 GTK_MENU_ITEM_GET_CLASS(menu_item)->toggle_size = GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item)->orig_toggle_size;
37d403aa
JS
1725 }
1726 }
1727
1728 /* Note that we actually need to do this for _all_ GtkPixmapMenuItem
1729 whenever the klass->toggle_size changes; but by doing it anytime
1730 this function is called, we get the same effect, just because of
1731 how the preferences option to show pixmaps works. Bogus, broken.
1732 */
2368dcda 1733 if (GTK_WIDGET_VISIBLE(GTK_WIDGET(menu_item)))
37d403aa
JS
1734 gtk_widget_queue_resize(GTK_WIDGET(menu_item));
1735}
1736
9e691f46 1737#endif // USE_MENU_BITMAPS
37d403aa 1738