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