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