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