]> git.saurik.com Git - wxWidgets.git/blame - src/gtk/menu.cpp
use IMPLEMENT_APP_CONSOLE and not IMPLEMENT_APP for console apps
[wxWidgets.git] / src / gtk / menu.cpp
CommitLineData
c801d85f 1/////////////////////////////////////////////////////////////////////////////
88a7a4e1 2// Name: src/gtk/menu.cpp
28fcfbfe 3// Purpose: implementation of wxMenuBar and wxMenu classes for wxGTK
c801d85f 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// For compilers that support precompilation, includes "wx.h".
11#include "wx/wxprec.h"
12
28fcfbfe
VZ
13#if wxUSE_MENUS
14
e1bf3ad3 15#include "wx/menu.h"
88a7a4e1
WS
16
17#ifndef WX_PRECOMP
18 #include "wx/intl.h"
e4db172a 19 #include "wx/log.h"
d281df50 20 #include "wx/frame.h"
0bca0373 21 #include "wx/bitmap.h"
7d02e0d6 22 #include "wx/app.h"
88a7a4e1
WS
23#endif
24
d281df50 25#include "wx/accel.h"
ab73fe8d 26#include "wx/stockitem.h"
9e691f46 27#include "wx/gtk/private.h"
b1f17bf0 28#include "wx/gtk/private/mnemonics.h"
9e691f46 29
9959e2b6
VZ
30// we use normal item but with a special id for the menu title
31static const int wxGTK_TITLE_ID = -3;
32
5e6f2fe9
VZ
33// forward declare it as it's used by wxMenuBar too when using Hildon
34extern "C"
35{
36 static void gtk_menu_clicked_callback(GtkWidget *widget, wxMenu *menu);
37}
6c76d1ce 38
6d971354 39#if wxUSE_ACCEL
19abd352 40static bool wxGetStockGtkAccelerator(const char *id, GdkModifierType *mod, guint *key);
98f29783 41static wxString GetGtkHotKey( const wxMenuItem& item );
717a57c2
VZ
42#endif
43
cdf003d4 44static void DoCommonMenuCallbackCode(wxMenu *menu, wxMenuEvent& event)
e6396ed4 45{
e6396ed4
JS
46 event.SetEventObject( menu );
47
a01fe3d6 48 wxEvtHandler* handler = menu->GetEventHandler();
937013e0 49 if (handler && handler->SafelyProcessEvent(event))
e6396ed4
JS
50 return;
51
52 wxWindow *win = menu->GetInvokingWindow();
cdf003d4 53 if (win)
937013e0 54 win->HandleWindowEvent( event );
cdf003d4
VZ
55}
56
c801d85f
KB
57//-----------------------------------------------------------------------------
58// wxMenuBar
59//-----------------------------------------------------------------------------
60
61IMPLEMENT_DYNAMIC_CLASS(wxMenuBar,wxWindow)
62
294ea16d 63void wxMenuBar::Init(size_t n, wxMenu *menus[], const wxString titles[], long style)
3502e687 64{
e8375af8 65 m_invokingWindow = NULL;
23280650 66
e2f3bc41
VZ
67#if wxUSE_LIBHILDON
68 // Hildon window uses a single menu instead of a menu bar, so wxMenuBar is
69 // the same as menu in this case
70 m_widget =
71 m_menubar = gtk_menu_new();
72#else // !wxUSE_LIBHILDON
e8375af8
VZ
73 if (!PreCreation( NULL, wxDefaultPosition, wxDefaultSize ) ||
74 !CreateBase( NULL, -1, wxDefaultPosition, wxDefaultSize, style, wxDefaultValidator, wxT("menubar") ))
4dcaf11a 75 {
223d09f6 76 wxFAIL_MSG( wxT("wxMenuBar creation failed") );
455fadaa 77 return;
4dcaf11a 78 }
3502e687 79
3502e687
RR
80 m_menubar = gtk_menu_bar_new();
81
82 if (style & wxMB_DOCKABLE)
83 {
84 m_widget = gtk_handle_box_new();
10bd1f7d
PC
85 gtk_container_add(GTK_CONTAINER(m_widget), m_menubar);
86 gtk_widget_show(m_menubar);
3502e687
RR
87 }
88 else
89 {
10bd1f7d 90 m_widget = m_menubar;
3502e687
RR
91 }
92
93 PostCreation();
c4608a8a 94
db434467 95 ApplyWidgetStyle();
e2f3bc41 96#endif // wxUSE_LIBHILDON/!wxUSE_LIBHILDON
294ea16d
VZ
97
98 for (size_t i = 0; i < n; ++i )
99 Append(menus[i], titles[i]);
3502e687
RR
100}
101
294ea16d 102wxMenuBar::wxMenuBar(size_t n, wxMenu *menus[], const wxString titles[], long style)
c801d85f 103{
294ea16d
VZ
104 Init(n, menus, titles, style);
105}
96fd301f 106
294ea16d
VZ
107wxMenuBar::wxMenuBar(long style)
108{
109 Init(0, NULL, NULL, style);
110}
c4608a8a 111
294ea16d
VZ
112wxMenuBar::wxMenuBar()
113{
114 Init(0, NULL, NULL, 0);
6de97a3b 115}
c801d85f 116
1e133b7d
RR
117wxMenuBar::~wxMenuBar()
118{
1e133b7d
RR
119}
120
8ef9a526
PC
121static void
122wxMenubarUnsetInvokingWindow(wxMenu* menu, wxWindow* win, GtkWindow* tlw = NULL)
5bd9e519
RR
123{
124 menu->SetInvokingWindow( (wxWindow*) NULL );
125
05b743ba 126 // support for native hot keys
8ef9a526
PC
127 if (menu->m_accel)
128 {
129 if (tlw == NULL)
130 tlw = GTK_WINDOW(wxGetTopLevelParent(win)->m_widget);
131 if (g_slist_find(menu->m_accel->acceleratables, tlw))
132 gtk_window_remove_accel_group(tlw, menu->m_accel);
133 }
05b743ba 134
222ed1d6 135 wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst();
5bd9e519
RR
136 while (node)
137 {
1987af7e 138 wxMenuItem *menuitem = node->GetData();
5bd9e519 139 if (menuitem->IsSubMenu())
8ef9a526 140 wxMenubarUnsetInvokingWindow(menuitem->GetSubMenu(), win, tlw);
1987af7e 141 node = node->GetNext();
5bd9e519
RR
142 }
143}
144
8ef9a526
PC
145static void
146wxMenubarSetInvokingWindow(wxMenu* menu, wxWindow* win, GtkWindow* tlw = NULL)
5bd9e519
RR
147{
148 menu->SetInvokingWindow( win );
149
6d971354 150 // support for native hot keys
8ef9a526
PC
151 if (menu->m_accel)
152 {
153 if (tlw == NULL)
154 tlw = GTK_WINDOW(wxGetTopLevelParent(win)->m_widget);
155 if (!g_slist_find(menu->m_accel->acceleratables, tlw))
156 gtk_window_add_accel_group(tlw, menu->m_accel);
157 }
5bd9e519 158
222ed1d6 159 wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst();
5bd9e519
RR
160 while (node)
161 {
1987af7e 162 wxMenuItem *menuitem = node->GetData();
5bd9e519 163 if (menuitem->IsSubMenu())
8ef9a526 164 wxMenubarSetInvokingWindow(menuitem->GetSubMenu(), win, tlw);
1987af7e 165 node = node->GetNext();
5bd9e519
RR
166 }
167}
168
169void wxMenuBar::SetInvokingWindow( wxWindow *win )
170{
9c884972 171 m_invokingWindow = win;
5bd9e519 172
222ed1d6 173 wxMenuList::compatibility_iterator node = m_menus.GetFirst();
5bd9e519
RR
174 while (node)
175 {
1987af7e 176 wxMenu *menu = node->GetData();
5bd9e519 177 wxMenubarSetInvokingWindow( menu, win );
1987af7e 178 node = node->GetNext();
5bd9e519
RR
179 }
180}
181
978af864
VZ
182void wxMenuBar::SetLayoutDirection(wxLayoutDirection dir)
183{
184 if ( dir == wxLayout_Default )
185 {
186 const wxWindow *const frame = GetFrame();
187 if ( frame )
188 {
189 // inherit layout from frame.
190 dir = frame->GetLayoutDirection();
191 }
192 else // use global layout
193 {
194 dir = wxTheApp->GetLayoutDirection();
195 }
196 }
197
198 if ( dir == wxLayout_Default )
199 return;
200
201 GTKSetLayout(m_menubar, dir);
202
203 // also set the layout of all menus we already have (new ones will inherit
204 // the current layout)
205 for ( wxMenuList::compatibility_iterator node = m_menus.GetFirst();
206 node;
207 node = node->GetNext() )
208 {
209 wxMenu *const menu = node->GetData();
210 menu->SetLayoutDirection(dir);
211 }
212}
213
214wxLayoutDirection wxMenuBar::GetLayoutDirection() const
215{
216 return GTKGetLayout(m_menubar);
217}
218
219void wxMenuBar::Attach(wxFrame *frame)
220{
221 wxMenuBarBase::Attach(frame);
222
223 SetLayoutDirection(wxLayout_Default);
224}
225
5bd9e519
RR
226void wxMenuBar::UnsetInvokingWindow( wxWindow *win )
227{
9c884972 228 m_invokingWindow = (wxWindow*) NULL;
9959e2b6 229
222ed1d6 230 wxMenuList::compatibility_iterator node = m_menus.GetFirst();
5bd9e519
RR
231 while (node)
232 {
1987af7e 233 wxMenu *menu = node->GetData();
5bd9e519 234 wxMenubarUnsetInvokingWindow( menu, win );
1987af7e 235 node = node->GetNext();
5bd9e519
RR
236 }
237}
238
3dfac970 239bool wxMenuBar::Append( wxMenu *menu, const wxString &title )
c801d85f 240{
f03ec224 241 if ( !wxMenuBarBase::Append( menu, title ) )
670f9935 242 return false;
f03ec224
VZ
243
244 return GtkAppend(menu, title);
245}
23280650 246
49826dab 247bool wxMenuBar::GtkAppend(wxMenu *menu, const wxString& title, int pos)
f03ec224 248{
6c76d1ce 249 menu->SetLayoutDirection(GetLayoutDirection());
1e133b7d 250
6c76d1ce
VZ
251#if wxUSE_LIBHILDON
252 // if the menu has only one item, append it directly to the top level menu
253 // instead of inserting a useless submenu
254 if ( menu->GetMenuItemCount() == 1 )
255 {
256 wxMenuItem * const item = menu->FindItemByPosition(0);
23280650 257
6c76d1ce
VZ
258 // remove both mnemonics and accelerator: neither is useful under Maemo
259 const wxString str(wxStripMenuCodes(item->GetItemLabel()));
96fd301f 260
6c76d1ce
VZ
261 if ( item->IsSubMenu() )
262 return GtkAppend(item->GetSubMenu(), str, pos);
263
264 menu->m_owner = gtk_menu_item_new_with_mnemonic( wxGTK_CONV( str ) );
9959e2b6 265
6c76d1ce
VZ
266 g_signal_connect(menu->m_owner, "activate",
267 G_CALLBACK(gtk_menu_clicked_callback), menu);
268 item->SetMenuItem(menu->m_owner);
269 }
270 else
271#endif // wxUSE_LIBHILDON/!wxUSE_LIBHILDON
272 {
273 const wxString str(wxConvertMnemonicsToGTK(title));
274
275 // This doesn't have much effect right now.
276 menu->SetTitle( str );
277
278 // The "m_owner" is the "menu item"
279 menu->m_owner = gtk_menu_item_new_with_mnemonic( wxGTK_CONV( str ) );
280
281 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu->m_owner), menu->m_menu );
282 }
283
284 gtk_widget_show( menu->m_owner );
9959e2b6 285
49826dab
RD
286 if (pos == -1)
287 gtk_menu_shell_append( GTK_MENU_SHELL(m_menubar), menu->m_owner );
288 else
289 gtk_menu_shell_insert( GTK_MENU_SHELL(m_menubar), menu->m_owner, pos );
9959e2b6 290
9c884972 291 // m_invokingWindow is set after wxFrame::SetMenuBar(). This call enables
2b5f62a0 292 // addings menu later on.
9c884972
RR
293 if (m_invokingWindow)
294 wxMenubarSetInvokingWindow( menu, m_invokingWindow );
3dfac970 295
670f9935 296 return true;
3dfac970
VZ
297}
298
299bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title)
300{
301 if ( !wxMenuBarBase::Insert(pos, menu, title) )
670f9935 302 return false;
3dfac970 303
6d971354 304 // TODO
9959e2b6 305
49826dab 306 if ( !GtkAppend(menu, title, (int)pos) )
670f9935 307 return false;
f03ec224 308
670f9935 309 return true;
3dfac970
VZ
310}
311
312wxMenu *wxMenuBar::Replace(size_t pos, wxMenu *menu, const wxString& title)
313{
f03ec224
VZ
314 // remove the old item and insert a new one
315 wxMenu *menuOld = Remove(pos);
316 if ( menuOld && !Insert(pos, menu, title) )
317 {
1d62a8b4 318 return (wxMenu*) NULL;
f03ec224 319 }
3dfac970 320
f03ec224
VZ
321 // either Insert() succeeded or Remove() failed and menuOld is NULL
322 return menuOld;
3dfac970
VZ
323}
324
325wxMenu *wxMenuBar::Remove(size_t pos)
326{
f03ec224
VZ
327 wxMenu *menu = wxMenuBarBase::Remove(pos);
328 if ( !menu )
1d62a8b4 329 return (wxMenu*) NULL;
f03ec224 330
6f536f31 331 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu->m_owner), NULL);
defc0789 332 gtk_container_remove(GTK_CONTAINER(m_menubar), menu->m_owner);
c4608a8a 333
1d62a8b4 334 gtk_widget_destroy( menu->m_owner );
defc0789 335 menu->m_owner = NULL;
c4608a8a 336
2b5f62a0 337 if (m_invokingWindow)
05b743ba 338 wxMenubarUnsetInvokingWindow( menu, m_invokingWindow );
e4161a2a 339
1d62a8b4 340 return menu;
6de97a3b 341}
96fd301f 342
716b7364 343static int FindMenuItemRecursive( const wxMenu *menu, const wxString &menuString, const wxString &itemString )
c801d85f 344{
b1f17bf0 345 if (wxMenuItem::GetLabelText(wxConvertMnemonicsFromGTK(menu->GetTitle())) == wxMenuItem::GetLabelText(menuString))
83624f79
RR
346 {
347 int res = menu->FindItem( itemString );
c626a8b7
VZ
348 if (res != wxNOT_FOUND)
349 return res;
83624f79 350 }
c626a8b7 351
222ed1d6 352 wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst();
83624f79
RR
353 while (node)
354 {
1987af7e 355 wxMenuItem *item = node->GetData();
83624f79
RR
356 if (item->IsSubMenu())
357 return FindMenuItemRecursive(item->GetSubMenu(), menuString, itemString);
2b1c162e 358
1987af7e 359 node = node->GetNext();
83624f79
RR
360 }
361
c626a8b7
VZ
362 return wxNOT_FOUND;
363}
364
c801d85f
KB
365int wxMenuBar::FindMenuItem( const wxString &menuString, const wxString &itemString ) const
366{
222ed1d6 367 wxMenuList::compatibility_iterator node = m_menus.GetFirst();
83624f79
RR
368 while (node)
369 {
1987af7e 370 wxMenu *menu = node->GetData();
83624f79 371 int res = FindMenuItemRecursive( menu, menuString, itemString);
1987af7e
VZ
372 if (res != -1)
373 return res;
374 node = node->GetNext();
83624f79 375 }
1987af7e
VZ
376
377 return wxNOT_FOUND;
6de97a3b 378}
c801d85f 379
c626a8b7 380// Find a wxMenuItem using its id. Recurses down into sub-menus
96fd301f 381static wxMenuItem* FindMenuItemByIdRecursive(const wxMenu* menu, int id)
716b7364 382{
717a57c2 383 wxMenuItem* result = menu->FindChildItem(id);
716b7364 384
222ed1d6 385 wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst();
c626a8b7 386 while ( node && result == NULL )
83624f79 387 {
1987af7e 388 wxMenuItem *item = node->GetData();
83624f79 389 if (item->IsSubMenu())
c626a8b7 390 {
83624f79 391 result = FindMenuItemByIdRecursive( item->GetSubMenu(), id );
c626a8b7 392 }
1987af7e 393 node = node->GetNext();
83624f79 394 }
96fd301f 395
83624f79 396 return result;
6de97a3b 397}
716b7364 398
3dfac970 399wxMenuItem* wxMenuBar::FindItem( int id, wxMenu **menuForItem ) const
716b7364 400{
83624f79 401 wxMenuItem* result = 0;
222ed1d6 402 wxMenuList::compatibility_iterator node = m_menus.GetFirst();
83624f79
RR
403 while (node && result == 0)
404 {
1987af7e 405 wxMenu *menu = node->GetData();
83624f79 406 result = FindMenuItemByIdRecursive( menu, id );
1987af7e 407 node = node->GetNext();
83624f79 408 }
c626a8b7 409
3dfac970
VZ
410 if ( menuForItem )
411 {
412 *menuForItem = result ? result->GetMenu() : (wxMenu *)NULL;
413 }
c626a8b7 414
3dfac970 415 return result;
bbe0af5b
RR
416}
417
3dfac970 418void wxMenuBar::EnableTop( size_t pos, bool flag )
bbe0af5b 419{
222ed1d6 420 wxMenuList::compatibility_iterator node = m_menus.Item( pos );
c626a8b7 421
223d09f6 422 wxCHECK_RET( node, wxT("menu not found") );
c626a8b7 423
3dfac970 424 wxMenu* menu = node->GetData();
c626a8b7
VZ
425
426 if (menu->m_owner)
427 gtk_widget_set_sensitive( menu->m_owner, flag );
bbe0af5b
RR
428}
429
52af3158 430wxString wxMenuBar::GetMenuLabel( size_t pos ) const
bbe0af5b 431{
222ed1d6 432 wxMenuList::compatibility_iterator node = m_menus.Item( pos );
c626a8b7 433
223d09f6 434 wxCHECK_MSG( node, wxT("invalid"), wxT("menu not found") );
c626a8b7 435
3dfac970 436 wxMenu* menu = node->GetData();
c626a8b7 437
b1f17bf0 438 return wxConvertMnemonicsFromGTK(menu->GetTitle());
bbe0af5b
RR
439}
440
52af3158 441void wxMenuBar::SetMenuLabel( size_t pos, const wxString& label )
bbe0af5b 442{
222ed1d6 443 wxMenuList::compatibility_iterator node = m_menus.Item( pos );
c626a8b7 444
223d09f6 445 wxCHECK_RET( node, wxT("menu not found") );
c626a8b7 446
3dfac970 447 wxMenu* menu = node->GetData();
c626a8b7 448
b1f17bf0 449 const wxString str(wxConvertMnemonicsToGTK(label));
f6bcfd97
BP
450
451 menu->SetTitle( str );
452
453 if (menu->m_owner)
8394218c 454 gtk_label_set_text_with_mnemonic( GTK_LABEL( GTK_BIN(menu->m_owner)->child), wxGTK_CONV(str) );
bbe0af5b
RR
455}
456
c801d85f 457//-----------------------------------------------------------------------------
cf7a7e13 458// "activate"
c801d85f
KB
459//-----------------------------------------------------------------------------
460
865bb325 461extern "C" {
6de97a3b 462static void gtk_menu_clicked_callback( GtkWidget *widget, wxMenu *menu )
c801d85f 463{
83624f79 464 int id = menu->FindMenuIdByMenuItem(widget);
96fd301f 465
83624f79 466 /* should find it for normal (not popup) menu */
1e6feb95
VZ
467 wxASSERT_MSG( (id != -1) || (menu->GetInvokingWindow() != NULL),
468 _T("menu item not found in gtk_menu_clicked_callback") );
96fd301f 469
c626a8b7
VZ
470 if (!menu->IsEnabled(id))
471 return;
96fd301f 472
4e6beae5
RD
473 wxMenuItem* item = menu->FindChildItem( id );
474 wxCHECK_RET( item, wxT("error in menu item callback") );
475
9959e2b6
VZ
476 if ( item->GetId() == wxGTK_TITLE_ID )
477 {
478 // ignore events from the menu title
479 return;
480 }
481
4e6beae5
RD
482 if (item->IsCheckable())
483 {
484 bool isReallyChecked = item->IsChecked(),
485 isInternallyChecked = item->wxMenuItemBase::IsChecked();
486
487 // ensure that the internal state is always consistent with what is
488 // shown on the screen
489 item->wxMenuItemBase::Check(isReallyChecked);
490
491 // we must not report the events for the radio button going up nor the
492 // events resulting from the calls to wxMenuItem::Check()
493 if ( (item->GetKind() == wxITEM_RADIO && !isReallyChecked) ||
494 (isInternallyChecked == isReallyChecked) )
495 {
496 return;
497 }
498 }
499
500
02822c8c
RD
501 // Is this menu on a menubar? (possibly nested)
502 wxFrame* frame = NULL;
6edf1107
DE
503 if(menu->IsAttached())
504 frame = menu->GetMenuBar()->GetFrame();
02822c8c 505
da0b5338
VZ
506 // FIXME: why do we have to call wxFrame::GetEventHandler() directly here?
507 // normally wxMenu::SendEvent() should be enough, if it doesn't work
508 // in wxGTK then we have a bug in wxMenu::GetInvokingWindow() which
509 // should be fixed instead of working around it here...
02822c8c 510 if (frame)
2d17d68f 511 {
4e6beae5
RD
512 // If it is attached then let the frame send the event.
513 // Don't call frame->ProcessCommand(id) because it toggles
514 // checkable items and we've already done that above.
515 wxCommandEvent commandEvent(wxEVT_COMMAND_MENU_SELECTED, id);
516 commandEvent.SetEventObject(frame);
517 if (item->IsCheckable())
518 commandEvent.SetInt(item->IsChecked());
519
937013e0 520 frame->HandleWindowEvent(commandEvent);
a01fe3d6
RD
521 }
522 else
523 {
4e6beae5 524 // otherwise let the menu have it
a01fe3d6 525 menu->SendEvent(id, item->IsCheckable() ? item->IsChecked() : -1);
2d17d68f 526 }
cf7a7e13 527}
865bb325 528}
cf7a7e13
RR
529
530//-----------------------------------------------------------------------------
531// "select"
532//-----------------------------------------------------------------------------
533
865bb325 534extern "C" {
cf7a7e13
RR
535static void gtk_menu_hilight_callback( GtkWidget *widget, wxMenu *menu )
536{
83624f79
RR
537 int id = menu->FindMenuIdByMenuItem(widget);
538
539 wxASSERT( id != -1 ); // should find it!
cf7a7e13 540
c626a8b7
VZ
541 if (!menu->IsEnabled(id))
542 return;
cf7a7e13 543
342b6a2f 544 wxMenuEvent event( wxEVT_MENU_HIGHLIGHT, id );
83624f79 545 event.SetEventObject( menu );
cf7a7e13 546
a01fe3d6 547 wxEvtHandler* handler = menu->GetEventHandler();
937013e0 548 if (handler && handler->SafelyProcessEvent(event))
c626a8b7 549 return;
6de97a3b 550
83624f79 551 wxWindow *win = menu->GetInvokingWindow();
937013e0 552 if (win) win->HandleWindowEvent( event );
6de97a3b 553}
865bb325 554}
c801d85f 555
cd743a6f
RR
556//-----------------------------------------------------------------------------
557// "deselect"
558//-----------------------------------------------------------------------------
559
865bb325 560extern "C" {
cd743a6f
RR
561static void gtk_menu_nolight_callback( GtkWidget *widget, wxMenu *menu )
562{
563 int id = menu->FindMenuIdByMenuItem(widget);
564
565 wxASSERT( id != -1 ); // should find it!
566
c626a8b7
VZ
567 if (!menu->IsEnabled(id))
568 return;
cd743a6f
RR
569
570 wxMenuEvent event( wxEVT_MENU_HIGHLIGHT, -1 );
571 event.SetEventObject( menu );
572
a01fe3d6 573 wxEvtHandler* handler = menu->GetEventHandler();
937013e0 574 if (handler && handler->SafelyProcessEvent(event))
c626a8b7 575 return;
cd743a6f
RR
576
577 wxWindow *win = menu->GetInvokingWindow();
c626a8b7 578 if (win)
937013e0 579 win->HandleWindowEvent( event );
cd743a6f 580}
865bb325 581}
cd743a6f 582
cf7a7e13 583//-----------------------------------------------------------------------------
db1b4961 584// wxMenuItem
cf7a7e13
RR
585//-----------------------------------------------------------------------------
586
7dbf5360 587IMPLEMENT_DYNAMIC_CLASS(wxMenuItem, wxObject)
974e8d94
VZ
588
589wxMenuItem *wxMenuItemBase::New(wxMenu *parentMenu,
590 int id,
591 const wxString& name,
592 const wxString& help,
d65c269b 593 wxItemKind kind,
974e8d94
VZ
594 wxMenu *subMenu)
595{
d65c269b 596 return new wxMenuItem(parentMenu, id, name, help, kind, subMenu);
974e8d94 597}
96fd301f 598
974e8d94
VZ
599wxMenuItem::wxMenuItem(wxMenu *parentMenu,
600 int id,
601 const wxString& text,
602 const wxString& help,
d65c269b 603 wxItemKind kind,
974e8d94 604 wxMenu *subMenu)
d65c269b 605 : wxMenuItemBase(parentMenu, id, text, help, kind, subMenu)
2368dcda 606{
092f7536 607 Init(text);
2368dcda
VZ
608}
609
efebabb7 610#if WXWIN_COMPATIBILITY_2_8
2368dcda
VZ
611wxMenuItem::wxMenuItem(wxMenu *parentMenu,
612 int id,
613 const wxString& text,
614 const wxString& help,
615 bool isCheckable,
616 wxMenu *subMenu)
617 : wxMenuItemBase(parentMenu, id, text, help,
618 isCheckable ? wxITEM_CHECK : wxITEM_NORMAL, subMenu)
619{
092f7536 620 Init(text);
2368dcda 621}
efebabb7 622#endif
2368dcda 623
092f7536 624void wxMenuItem::Init(const wxString& text)
c801d85f 625{
83624f79 626 m_menuItem = (GtkWidget *) NULL;
974e8d94 627
092f7536 628 DoSetText(text);
6de97a3b 629}
c801d85f 630
d1b15f03
RR
631wxMenuItem::~wxMenuItem()
632{
633 // don't delete menu items, the menus take care of that
634}
635
717a57c2 636// return the menu item text without any menu accels
3b59cdbf 637/* static */
52af3158 638
52af3158 639wxString wxMenuItemBase::GetLabelText(const wxString& text)
717a57c2 640{
52af3158
JS
641 // The argument to this function will now always be in wxWidgets standard label
642 // format, not GTK+ format, so we do what the other ports do.
643
644 return wxStripMenuCodes(text);
645
646#if 0
717a57c2 647 wxString label;
2368dcda 648
3b59cdbf 649 for ( const wxChar *pc = text.c_str(); *pc; pc++ )
717a57c2 650 {
f0b72e0d
RR
651 if ( *pc == wxT('\t'))
652 break;
9959e2b6 653
7cf4f7e2 654 if ( *pc == wxT('_') )
717a57c2 655 {
2b5f62a0 656 // GTK 1.2 escapes "xxx_xxx" to "xxx__xxx"
d76419bd
RR
657 pc++;
658 label += *pc;
659 continue;
660 }
2368dcda 661
2b5f62a0 662 if ( *pc == wxT('\\') )
d76419bd 663 {
2b5f62a0
VZ
664 // GTK 2.0 escapes "xxx/xxx" to "xxx\/xxx"
665 pc++;
666 label += *pc;
717a57c2
VZ
667 continue;
668 }
669
7cf4f7e2
GD
670 if ( (*pc == wxT('&')) && (*(pc+1) != wxT('&')) )
671 {
672 // wxMSW escapes "&"
673 // "&" is doubled to indicate "&" instead of accelerator
674 continue;
675 }
a01fe3d6 676
717a57c2
VZ
677 label += *pc;
678 }
a01fe3d6 679
52af3158 680 // wxPrintf( wxT("GetLabelText(): text %s label %s\n"), text.c_str(), label.c_str() );
d9e403cc 681
717a57c2 682 return label;
52af3158 683#endif
717a57c2
VZ
684}
685
52af3158
JS
686wxString wxMenuItem::GetItemLabel() const
687{
b1f17bf0 688 wxString label = wxConvertMnemonicsFromGTK(m_text);
a738f87c 689 if (!m_hotKey.IsEmpty())
b1f17bf0 690 label << "\t" << m_hotKey;
a738f87c 691 return label;
52af3158
JS
692}
693
694void wxMenuItem::SetItemLabel( const wxString& str )
717a57c2 695{
ee0a94cf
RR
696 // cache some data which must be used later
697 bool isstock = wxIsStockID(GetId());
698 const char *stockid = NULL;
699 if (isstock)
700 stockid = wxGetStockGtkID(GetId());
701
5869f93f
JS
702 // Some optimization to avoid flicker
703 wxString oldLabel = m_text;
98f29783 704 oldLabel = wxStripMenuCodes(oldLabel);
5869f93f 705 oldLabel.Replace(wxT("_"), wxT(""));
98f29783 706 wxString label1 = wxStripMenuCodes(str);
6c309653 707#if wxUSE_ACCEL
aac7dbf3 708 wxString oldhotkey = m_hotKey; // Store the old hotkey in Ctrl-foo format
5f11fef5 709 wxCharBuffer oldbuf = wxGTK_CONV_SYS( GetGtkHotKey(*this) ); // and as <control>foo
6c309653 710#endif // wxUSE_ACCEL
a01fe3d6 711
717a57c2 712 DoSetText(str);
354aa1e3 713
6c309653 714#if wxUSE_ACCEL
88d19775 715 if (oldLabel == label1 &&
aac7dbf3 716 oldhotkey == m_hotKey) // Make sure we can change a hotkey even if the label is unaltered
98f29783
RR
717 return;
718
354aa1e3
RR
719 if (m_menuItem)
720 {
ee0a94cf
RR
721 // stock menu items can have empty labels:
722 wxString text = m_text;
723 if (text.IsEmpty() && !IsSeparator())
724 {
725 wxASSERT_MSG(isstock, wxT("A non-stock menu item with an empty label?"));
726 text = wxGetStockLabel(GetId());
727
728 // need & => _ conversion
729 text = GTKProcessMenuItemLabel(text, NULL);
730 }
731
aac7dbf3
PC
732 GtkLabel* label = GTK_LABEL(GTK_BIN(m_menuItem)->child);
733 gtk_label_set_text_with_mnemonic(label, wxGTK_CONV_SYS(text));
354aa1e3 734 }
98f29783 735
ee0a94cf 736 // remove old accelerator from our parent's accelerator group, if present
98f29783
RR
737 guint accel_key;
738 GdkModifierType accel_mods;
ee0a94cf 739 if (oldbuf[(size_t)0] != '\0')
98f29783 740 {
ee0a94cf
RR
741 gtk_accelerator_parse( (const char*) oldbuf, &accel_key, &accel_mods);
742 if (accel_key != 0)
743 {
10bd1f7d 744 gtk_widget_remove_accelerator(m_menuItem,
ee0a94cf
RR
745 m_parentMenu->m_accel,
746 accel_key,
747 accel_mods );
748 }
749 }
750 else if (isstock)
751 {
752 // if the accelerator was taken from a stock ID, just get it back from GTK+ stock
753 if (wxGetStockGtkAccelerator(stockid, &accel_mods, &accel_key))
10bd1f7d 754 gtk_widget_remove_accelerator( m_menuItem,
ee0a94cf
RR
755 m_parentMenu->m_accel,
756 accel_key,
757 accel_mods );
98f29783
RR
758 }
759
ee0a94cf 760 // add new accelerator to our parent's accelerator group
5f11fef5 761 wxCharBuffer buf = wxGTK_CONV_SYS( GetGtkHotKey(*this) );
ee0a94cf 762 if (buf[(size_t)0] != '\0')
98f29783 763 {
ee0a94cf
RR
764 gtk_accelerator_parse( (const char*) buf, &accel_key, &accel_mods);
765 if (accel_key != 0)
766 {
10bd1f7d 767 gtk_widget_add_accelerator( m_menuItem,
ee0a94cf
RR
768 "activate",
769 m_parentMenu->m_accel,
770 accel_key,
771 accel_mods,
772 GTK_ACCEL_VISIBLE);
773 }
774 }
775 else if (isstock)
776 {
777 // if the accelerator was taken from a stock ID, just get it back from GTK+ stock
778 if (wxGetStockGtkAccelerator(stockid, &accel_mods, &accel_key))
10bd1f7d 779 gtk_widget_remove_accelerator( m_menuItem,
ee0a94cf
RR
780 m_parentMenu->m_accel,
781 accel_key,
782 accel_mods );
98f29783 783 }
19abd352 784#endif // wxUSE_ACCEL
354aa1e3
RR
785}
786
ee0a94cf 787// NOTE: this function is different from the similar functions GTKProcessMnemonics()
52af3158 788// implemented in control.cpp and from wxMenuItemBase::GetLabelText...
ee0a94cf
RR
789// so there's no real code duplication
790wxString wxMenuItem::GTKProcessMenuItemLabel(const wxString& str, wxString *hotKey)
716b7364 791{
ee0a94cf
RR
792 wxString text;
793
2b5f62a0 794 // '\t' is the deliminator indicating a hot key
e0a050e3
VS
795 wxString::const_iterator pc = str.begin();
796 while ( pc != str.end() && *pc != wxT('\t') )
83624f79 797 {
e0a050e3 798 if (*pc == wxT('&'))
23280650 799 {
e0a050e3
VS
800 wxString::const_iterator next = pc + 1;
801 if (next != str.end() && *next == wxT('&'))
802 {
803 // "&" is doubled to indicate "&" instead of accelerator
804 ++pc;
805 text << wxT('&');
806 }
807 else
808 {
809 text << wxT('_');
810 }
572d7461 811 }
2b5f62a0
VZ
812 else if ( *pc == wxT('_') ) // escape underscores
813 {
ee0a94cf 814 text << wxT("__");
2b5f62a0 815 }
9959e2b6 816 else
23280650 817 {
ee0a94cf 818 text << *pc;
2b5f62a0
VZ
819 }
820 ++pc;
83624f79 821 }
a01fe3d6 822
ee0a94cf 823 if (hotKey)
d7dbc98a 824 {
ee0a94cf
RR
825 hotKey->Empty();
826 if(*pc == wxT('\t'))
827 {
afd83fb7 828 ++pc;
e0a050e3 829 hotKey->assign(pc, str.end());
ee0a94cf 830 }
d7dbc98a 831 }
88d19775 832
ee0a94cf
RR
833 return text;
834}
835
836// it's valid for this function to be called even if m_menuItem == NULL
837void wxMenuItem::DoSetText( const wxString& str )
838{
839 m_text.Empty();
840 m_text = GTKProcessMenuItemLabel(str, &m_hotKey);
716b7364
RR
841}
842
717a57c2
VZ
843#if wxUSE_ACCEL
844
845wxAcceleratorEntry *wxMenuItem::GetAccel() const
846{
aac7dbf3 847 if (m_hotKey.empty())
717a57c2
VZ
848 {
849 // nothing
90527a50 850 return NULL;
717a57c2
VZ
851 }
852
90527a50
VZ
853 // accelerator parsing code looks for them after a TAB, so insert a dummy
854 // one here
717a57c2 855 wxString label;
aac7dbf3 856 label << wxT('\t') << m_hotKey;
717a57c2 857
90527a50 858 return wxAcceleratorEntry::Create(label);
717a57c2
VZ
859}
860
861#endif // wxUSE_ACCEL
862
96fd301f 863void wxMenuItem::Check( bool check )
716b7364 864{
223d09f6 865 wxCHECK_RET( m_menuItem, wxT("invalid menu item") );
db1b4961 866
974e8d94
VZ
867 if (check == m_isChecked)
868 return;
2d17d68f 869
974e8d94 870 wxMenuItemBase::Check( check );
0472ece7 871
24bcaec3 872 switch ( GetKind() )
0472ece7 873 {
24bcaec3
VZ
874 case wxITEM_CHECK:
875 case wxITEM_RADIO:
8394218c 876 gtk_check_menu_item_set_active( (GtkCheckMenuItem*)m_menuItem, (gint)check );
24bcaec3
VZ
877 break;
878
879 default:
880 wxFAIL_MSG( _T("can't check this item") );
0472ece7 881 }
716b7364
RR
882}
883
8bbe427f
VZ
884void wxMenuItem::Enable( bool enable )
885{
223d09f6 886 wxCHECK_RET( m_menuItem, wxT("invalid menu item") );
db1b4961 887
83624f79 888 gtk_widget_set_sensitive( m_menuItem, enable );
974e8d94 889 wxMenuItemBase::Enable( enable );
a9c96bcc
RR
890}
891
96fd301f 892bool wxMenuItem::IsChecked() const
716b7364 893{
670f9935 894 wxCHECK_MSG( m_menuItem, false, wxT("invalid menu item") );
db1b4961 895
670f9935 896 wxCHECK_MSG( IsCheckable(), false,
974e8d94 897 wxT("can't get state of uncheckable item!") );
96fd301f 898
974e8d94 899 return ((GtkCheckMenuItem*)m_menuItem)->active != 0;
716b7364
RR
900}
901
db1b4961 902//-----------------------------------------------------------------------------
83624f79 903// wxMenu
db1b4961
RR
904//-----------------------------------------------------------------------------
905
3ed946f2
PC
906extern "C" {
907// "map" from m_menu
908static void menu_map(GtkWidget*, wxMenu* menu)
909{
910 wxMenuEvent event(wxEVT_MENU_OPEN, menu->m_popupShown ? -1 : 0, menu);
911 DoCommonMenuCallbackCode(menu, event);
912}
913
914// "hide" from m_menu
915static void menu_hide(GtkWidget*, wxMenu* menu)
916{
917 wxMenuEvent event(wxEVT_MENU_CLOSE, menu->m_popupShown ? -1 : 0, menu);
918 menu->m_popupShown = false;
919 DoCommonMenuCallbackCode(menu, event);
920}
921}
922
c801d85f
KB
923IMPLEMENT_DYNAMIC_CLASS(wxMenu,wxEvtHandler)
924
717a57c2 925void wxMenu::Init()
c801d85f 926{
3ed946f2
PC
927 m_popupShown = false;
928
034be888 929 m_accel = gtk_accel_group_new();
6d971354 930 m_menu = gtk_menu_new();
defc0789
VS
931 // NB: keep reference to the menu so that it is not destroyed behind
932 // our back by GTK+ e.g. when it is removed from menubar:
0f35e441
PC
933 g_object_ref(m_menu);
934 gtk_object_sink(GTK_OBJECT(m_menu));
8bbe427f 935
2b1c162e 936 m_owner = (GtkWidget*) NULL;
2b2edbed 937
d9e403cc
RR
938 // Tearoffs are entries, just like separators. So if we want this
939 // menu to be a tear-off one, we just append a tearoff entry
940 // immediately.
9959e2b6 941 if ( m_style & wxMENU_TEAROFF )
2b2edbed 942 {
9959e2b6 943 GtkWidget *tearoff = gtk_tearoff_menu_item_new();
6d971354 944
1ab9e06d 945 gtk_menu_shell_append(GTK_MENU_SHELL(m_menu), tearoff);
9959e2b6 946 }
6d971354 947
9959e2b6 948 m_prevRadio = NULL;
c801d85f 949
717a57c2 950 // append the title as the very first entry if we have it
9959e2b6 951 if ( !m_title.empty() )
d1b15f03 952 {
9959e2b6 953 Append(wxGTK_TITLE_ID, m_title);
717a57c2 954 AppendSeparator();
d1b15f03 955 }
3ed946f2
PC
956
957 // "show" occurs for sub-menus which are not showing, so use "map" instead
958 g_signal_connect(m_menu, "map", G_CALLBACK(menu_map), this);
959 g_signal_connect(m_menu, "hide", G_CALLBACK(menu_hide), this);
717a57c2 960}
15a2076a 961
717a57c2
VZ
962wxMenu::~wxMenu()
963{
3ed946f2
PC
964 // see wxMenu::Init
965 g_object_unref(m_menu);
52af3158 966
3ed946f2
PC
967 // if the menu is inserted in another menu at this time, there was
968 // one more reference to it:
969 if (m_owner)
970 gtk_widget_destroy(m_menu);
f0378929 971
3ed946f2 972 g_object_unref(m_accel);
c2dd8380
GL
973}
974
978af864
VZ
975void wxMenu::SetLayoutDirection(const wxLayoutDirection dir)
976{
977 if ( m_owner )
978 wxWindow::GTKSetLayout(m_owner, dir);
979 //else: will be called later by wxMenuBar again
980}
981
982wxLayoutDirection wxMenu::GetLayoutDirection() const
983{
984 return wxWindow::GTKGetLayout(m_owner);
985}
986
49826dab 987bool wxMenu::GtkAppend(wxMenuItem *mitem, int pos)
c2dd8380 988{
717a57c2 989 GtkWidget *menuItem;
96fd301f 990
ee0a94cf 991 // cache some data used later
84c51319 992 wxString text = mitem->wxMenuItemBase::GetItemLabel();
ee0a94cf
RR
993 int id = mitem->GetId();
994 bool isstock = wxIsStockID(id);
995 const char *stockid = NULL;
996 if (isstock)
997 stockid = wxGetStockGtkID(mitem->GetId());
998
999 // stock menu items can have an empty label
1000 if (text.IsEmpty() && !mitem->IsSeparator())
1001 {
1002 wxASSERT_MSG(isstock, wxT("A non-stock menu item with an empty label?"));
1003 text = wxGetStockLabel(id);
1004
1005 // need & => _ conversion
1006 text = wxMenuItem::GTKProcessMenuItemLabel(text, NULL);
1007 }
e31126cb 1008
717a57c2
VZ
1009 if ( mitem->IsSeparator() )
1010 {
6d971354 1011 menuItem = gtk_separator_menu_item_new();
45813bad 1012 m_prevRadio = NULL;
837904f2 1013 }
ab73fe8d 1014 else if ( mitem->GetBitmap().Ok() ||
ee0a94cf 1015 (mitem->GetKind() == wxITEM_NORMAL && isstock) )
37d403aa 1016 {
ab73fe8d 1017 wxBitmap bitmap(mitem->GetBitmap());
9959e2b6 1018
5f11fef5 1019 menuItem = gtk_image_menu_item_new_with_mnemonic( wxGTK_CONV_SYS( text ) );
9959e2b6 1020
b1ad1424 1021 GtkWidget *image;
ab73fe8d 1022 if ( !bitmap.Ok() )
b1ad1424 1023 {
ab73fe8d
VZ
1024 // use stock bitmap for this item if available on the assumption
1025 // that it never hurts to follow GTK+ conventions more closely
ee0a94cf
RR
1026 image = stockid ? gtk_image_new_from_stock(stockid, GTK_ICON_SIZE_MENU)
1027 : NULL;
b1ad1424 1028 }
ab73fe8d 1029 else // we have a custom bitmap
b1ad1424 1030 {
ab73fe8d
VZ
1031 wxASSERT_MSG( mitem->GetKind() == wxITEM_NORMAL,
1032 _T("only normal menu items can have bitmaps") );
1033
1034 if ( bitmap.HasPixbuf() )
1035 {
1036 image = gtk_image_new_from_pixbuf(bitmap.GetPixbuf());
1037 }
1038 else
1039 {
1040 GdkPixmap *gdk_pixmap = bitmap.GetPixmap();
1041 GdkBitmap *gdk_bitmap = bitmap.GetMask() ?
1042 bitmap.GetMask()->GetBitmap() :
1043 (GdkBitmap*) NULL;
1044 image = gtk_image_new_from_pixmap( gdk_pixmap, gdk_bitmap );
1045 }
b1ad1424 1046 }
88d19775 1047
ab73fe8d
VZ
1048 if ( image )
1049 {
1050 gtk_widget_show(image);
9959e2b6 1051
ab73fe8d
VZ
1052 gtk_image_menu_item_set_image( GTK_IMAGE_MENU_ITEM(menuItem), image );
1053 }
9959e2b6 1054
6d971354
RR
1055 m_prevRadio = NULL;
1056 }
717a57c2
VZ
1057 else // a normal item
1058 {
52af3158 1059 // NB: 'text' variable has "_" instead of "&" after mitem->SetItemLabel()
ee0a94cf 1060 // so don't use it
717a57c2 1061
d65c269b
VZ
1062 switch ( mitem->GetKind() )
1063 {
546bfbea 1064 case wxITEM_CHECK:
6d971354 1065 {
5f11fef5 1066 menuItem = gtk_check_menu_item_new_with_mnemonic( wxGTK_CONV_SYS( text ) );
6d971354 1067 m_prevRadio = NULL;
d65c269b 1068 break;
6d971354 1069 }
d65c269b 1070
546bfbea 1071 case wxITEM_RADIO:
6d971354
RR
1072 {
1073 GSList *group = NULL;
1074 if ( m_prevRadio == NULL )
d65c269b
VZ
1075 {
1076 // start of a new radio group
5f11fef5
VZ
1077 m_prevRadio = menuItem =
1078 gtk_radio_menu_item_new_with_mnemonic( group, wxGTK_CONV_SYS( text ) );
d65c269b
VZ
1079 }
1080 else // continue the radio group
1081 {
6d971354 1082 group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (m_prevRadio));
5f11fef5
VZ
1083 m_prevRadio = menuItem =
1084 gtk_radio_menu_item_new_with_mnemonic( group, wxGTK_CONV_SYS( text ) );
d65c269b 1085 }
c0d0a552 1086 break;
6d971354 1087 }
d65c269b
VZ
1088
1089 default:
1090 wxFAIL_MSG( _T("unexpected menu item kind") );
1091 // fall through
1092
546bfbea 1093 case wxITEM_NORMAL:
6d971354 1094 {
5f11fef5 1095 menuItem = gtk_menu_item_new_with_mnemonic( wxGTK_CONV_SYS( text ) );
6d971354 1096 m_prevRadio = NULL;
d65c269b 1097 break;
6d971354 1098 }
d65c269b
VZ
1099 }
1100
6d971354 1101 }
9959e2b6 1102
6c309653 1103#if wxUSE_ACCEL
6d971354
RR
1104 guint accel_key;
1105 GdkModifierType accel_mods;
5f11fef5 1106 wxCharBuffer buf = wxGTK_CONV_SYS( GetGtkHotKey(*mitem) );
9959e2b6 1107
ee0a94cf
RR
1108 if (buf[(size_t)0] != '\0')
1109 {
1110 gtk_accelerator_parse( (const char*) buf, &accel_key, &accel_mods);
1111 if (accel_key != 0)
1112 {
10bd1f7d 1113 gtk_widget_add_accelerator (menuItem,
ee0a94cf
RR
1114 "activate",
1115 m_accel,
1116 accel_key,
1117 accel_mods,
1118 GTK_ACCEL_VISIBLE);
1119 }
1120 }
1121 else if (isstock)
6d971354 1122 {
ee0a94cf
RR
1123 // if the accelerator was taken from a stock ID, just get it back from GTK+ stock
1124 if (wxGetStockGtkAccelerator(stockid, &accel_mods, &accel_key))
10bd1f7d 1125 gtk_widget_add_accelerator( menuItem,
ee0a94cf
RR
1126 "activate",
1127 m_accel,
1128 accel_key,
1129 accel_mods,
1130 GTK_ACCEL_VISIBLE);
717a57c2 1131 }
19abd352 1132#endif // wxUSE_ACCEL
9959e2b6 1133
e31126cb
RR
1134 if (pos == -1)
1135 gtk_menu_shell_append(GTK_MENU_SHELL(m_menu), menuItem);
1136 else
1137 gtk_menu_shell_insert(GTK_MENU_SHELL(m_menu), menuItem, pos);
1138
6d971354 1139 gtk_widget_show( menuItem );
23280650 1140
717a57c2
VZ
1141 if ( !mitem->IsSeparator() )
1142 {
2b5f62a0 1143 wxASSERT_MSG( menuItem, wxT("invalid menuitem") );
a01fe3d6 1144
9fa72bd2
MR
1145 g_signal_connect (menuItem, "select",
1146 G_CALLBACK (gtk_menu_hilight_callback), this);
1147 g_signal_connect (menuItem, "deselect",
1148 G_CALLBACK (gtk_menu_nolight_callback), this);
e31126cb 1149
88d19775
MR
1150 if ( mitem->IsSubMenu() && mitem->GetKind() != wxITEM_RADIO && mitem->GetKind() != wxITEM_CHECK )
1151 {
e31126cb
RR
1152 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menuItem), mitem->GetSubMenu()->m_menu );
1153
1154 gtk_widget_show( mitem->GetSubMenu()->m_menu );
1155
1156 // if adding a submenu to a menu already existing in the menu bar, we
1157 // must set invoking window to allow processing events from this
1158 // submenu
1159 if ( m_invokingWindow )
1160 wxMenubarSetInvokingWindow(mitem->GetSubMenu(), m_invokingWindow);
88d19775
MR
1161 }
1162 else
1163 {
9fa72bd2
MR
1164 g_signal_connect (menuItem, "activate",
1165 G_CALLBACK (gtk_menu_clicked_callback),
1166 this);
88d19775 1167 }
717a57c2 1168 }
23280650 1169
837904f2 1170 mitem->SetMenuItem(menuItem);
837904f2 1171
6d971354 1172 if (ms_locked)
d65c269b 1173 {
6d971354
RR
1174 // This doesn't even exist!
1175 // gtk_widget_lock_accelerators(mitem->GetMenuItem());
d65c269b 1176 }
d65c269b 1177
670f9935 1178 return true;
6de97a3b 1179}
c801d85f 1180
9add9367 1181wxMenuItem* wxMenu::DoAppend(wxMenuItem *mitem)
828f655f 1182{
9add9367
RD
1183 if (!GtkAppend(mitem))
1184 return NULL;
9959e2b6 1185
9add9367 1186 return wxMenuBase::DoAppend(mitem);
c33c4050
RR
1187}
1188
9add9367 1189wxMenuItem* wxMenu::DoInsert(size_t pos, wxMenuItem *item)
c33c4050 1190{
717a57c2 1191 if ( !wxMenuBase::DoInsert(pos, item) )
9add9367 1192 return NULL;
c626a8b7 1193
6d971354 1194 // TODO
49826dab 1195 if ( !GtkAppend(item, (int)pos) )
9add9367 1196 return NULL;
32db328c 1197
9add9367 1198 return item;
c33c4050
RR
1199}
1200
717a57c2 1201wxMenuItem *wxMenu::DoRemove(wxMenuItem *item)
c33c4050 1202{
717a57c2 1203 if ( !wxMenuBase::DoRemove(item) )
8d749001
VZ
1204 return NULL;
1205
1206 GtkWidget * const mitem = item->GetMenuItem();
1207 if ( m_prevRadio == mitem )
1208 {
1209 // deleting an item starts a new radio group (has to as we shouldn't
1210 // keep a deleted pointer anyhow)
1211 m_prevRadio = NULL;
1212 }
c626a8b7 1213
afd83fb7 1214 gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem), NULL);
e828d3fc
PC
1215 gtk_widget_destroy(mitem);
1216 item->SetMenuItem(NULL);
c626a8b7 1217
717a57c2 1218 return item;
c33c4050
RR
1219}
1220
96fd301f
VZ
1221int wxMenu::FindMenuIdByMenuItem( GtkWidget *menuItem ) const
1222{
222ed1d6 1223 wxMenuItemList::compatibility_iterator node = m_items.GetFirst();
83624f79
RR
1224 while (node)
1225 {
b1d4dd7a 1226 wxMenuItem *item = node->GetData();
83624f79 1227 if (item->GetMenuItem() == menuItem)
88d19775 1228 return item->GetId();
b1d4dd7a 1229 node = node->GetNext();
83624f79 1230 }
96fd301f 1231
c626a8b7 1232 return wxNOT_FOUND;
6de97a3b 1233}
c801d85f 1234
978af864
VZ
1235void wxMenu::Attach(wxMenuBarBase *menubar)
1236{
1237 wxMenuBase::Attach(menubar);
1238
1239 // inherit layout direction from menubar.
1240 SetLayoutDirection(menubar->GetLayoutDirection());
1241}
1242
717a57c2
VZ
1243// ----------------------------------------------------------------------------
1244// helpers
1245// ----------------------------------------------------------------------------
1246
6d971354 1247#if wxUSE_ACCEL
a070d8ce 1248
98f29783 1249static wxString GetGtkHotKey( const wxMenuItem& item )
c801d85f 1250{
717a57c2
VZ
1251 wxString hotkey;
1252
1253 wxAcceleratorEntry *accel = item.GetAccel();
1254 if ( accel )
83624f79 1255 {
717a57c2
VZ
1256 int flags = accel->GetFlags();
1257 if ( flags & wxACCEL_ALT )
1258 hotkey += wxT("<alt>");
1259 if ( flags & wxACCEL_CTRL )
1260 hotkey += wxT("<control>");
1261 if ( flags & wxACCEL_SHIFT )
1262 hotkey += wxT("<shift>");
1263
1264 int code = accel->GetKeyCode();
1265 switch ( code )
c626a8b7 1266 {
717a57c2
VZ
1267 case WXK_F1:
1268 case WXK_F2:
1269 case WXK_F3:
1270 case WXK_F4:
1271 case WXK_F5:
1272 case WXK_F6:
1273 case WXK_F7:
1274 case WXK_F8:
1275 case WXK_F9:
1276 case WXK_F10:
1277 case WXK_F11:
1278 case WXK_F12:
bbcd4085
RR
1279 case WXK_F13:
1280 case WXK_F14:
1281 case WXK_F15:
1282 case WXK_F16:
1283 case WXK_F17:
1284 case WXK_F18:
1285 case WXK_F19:
1286 case WXK_F20:
1287 case WXK_F21:
1288 case WXK_F22:
1289 case WXK_F23:
1290 case WXK_F24:
04cc1e93 1291 hotkey += wxString::Format(wxT("F%d"), code - WXK_F1 + 1);
717a57c2 1292 break;
2368dcda 1293
a070d8ce
VZ
1294 // TODO: we should use gdk_keyval_name() (a.k.a.
1295 // XKeysymToString) here as well as hardcoding the keysym
1296 // names this might be not portable
bbcd4085 1297 case WXK_INSERT:
3ca6a5f0
BP
1298 hotkey << wxT("Insert" );
1299 break;
1300 case WXK_DELETE:
1301 hotkey << wxT("Delete" );
1302 break;
2b5f62a0
VZ
1303 case WXK_UP:
1304 hotkey << wxT("Up" );
1305 break;
1306 case WXK_DOWN:
1307 hotkey << wxT("Down" );
1308 break;
1309 case WXK_PAGEUP:
f005ea42 1310 hotkey << wxT("Page_Up" );
2b5f62a0
VZ
1311 break;
1312 case WXK_PAGEDOWN:
f005ea42 1313 hotkey << wxT("Page_Down" );
2b5f62a0
VZ
1314 break;
1315 case WXK_LEFT:
1316 hotkey << wxT("Left" );
1317 break;
1318 case WXK_RIGHT:
1319 hotkey << wxT("Right" );
1320 break;
1321 case WXK_HOME:
1322 hotkey << wxT("Home" );
1323 break;
1324 case WXK_END:
1325 hotkey << wxT("End" );
1326 break;
1327 case WXK_RETURN:
1328 hotkey << wxT("Return" );
1329 break;
bbcd4085
RR
1330 case WXK_BACK:
1331 hotkey << wxT("BackSpace" );
1332 break;
1333 case WXK_TAB:
1334 hotkey << wxT("Tab" );
1335 break;
1336 case WXK_ESCAPE:
1337 hotkey << wxT("Esc" );
1338 break;
1339 case WXK_SPACE:
1340 hotkey << wxT("space" );
1341 break;
1342 case WXK_MULTIPLY:
1343 hotkey << wxT("Multiply" );
1344 break;
1345 case WXK_ADD:
1346 hotkey << wxT("Add" );
1347 break;
1348 case WXK_SEPARATOR:
1349 hotkey << wxT("Separator" );
1350 break;
1351 case WXK_SUBTRACT:
1352 hotkey << wxT("Subtract" );
1353 break;
1354 case WXK_DECIMAL:
1355 hotkey << wxT("Decimal" );
1356 break;
1357 case WXK_DIVIDE:
1358 hotkey << wxT("Divide" );
1359 break;
1360 case WXK_CANCEL:
1361 hotkey << wxT("Cancel" );
1362 break;
1363 case WXK_CLEAR:
1364 hotkey << wxT("Clear" );
1365 break;
1366 case WXK_MENU:
1367 hotkey << wxT("Menu" );
1368 break;
1369 case WXK_PAUSE:
1370 hotkey << wxT("Pause" );
1371 break;
1372 case WXK_CAPITAL:
1373 hotkey << wxT("Capital" );
1374 break;
1375 case WXK_SELECT:
1376 hotkey << wxT("Select" );
1377 break;
1378 case WXK_PRINT:
1379 hotkey << wxT("Print" );
1380 break;
1381 case WXK_EXECUTE:
1382 hotkey << wxT("Execute" );
1383 break;
1384 case WXK_SNAPSHOT:
1385 hotkey << wxT("Snapshot" );
1386 break;
1387 case WXK_HELP:
1388 hotkey << wxT("Help" );
1389 break;
1390 case WXK_NUMLOCK:
1391 hotkey << wxT("Num_Lock" );
1392 break;
1393 case WXK_SCROLL:
1394 hotkey << wxT("Scroll_Lock" );
1395 break;
1396 case WXK_NUMPAD_INSERT:
1397 hotkey << wxT("KP_Insert" );
1398 break;
1399 case WXK_NUMPAD_DELETE:
1400 hotkey << wxT("KP_Delete" );
1401 break;
1402 case WXK_NUMPAD_SPACE:
1403 hotkey << wxT("KP_Space" );
1404 break;
1405 case WXK_NUMPAD_TAB:
1406 hotkey << wxT("KP_Tab" );
1407 break;
1408 case WXK_NUMPAD_ENTER:
1409 hotkey << wxT("KP_Enter" );
1410 break;
1411 case WXK_NUMPAD_F1: case WXK_NUMPAD_F2: case WXK_NUMPAD_F3:
1412 case WXK_NUMPAD_F4:
1413 hotkey += wxString::Format(wxT("KP_F%d"), code - WXK_NUMPAD_F1 + 1);
1414 break;
1415 case WXK_NUMPAD_HOME:
1416 hotkey << wxT("KP_Home" );
1417 break;
1418 case WXK_NUMPAD_LEFT:
1419 hotkey << wxT("KP_Left" );
1420 break;
1421 case WXK_NUMPAD_UP:
1422 hotkey << wxT("KP_Up" );
1423 break;
1424 case WXK_NUMPAD_RIGHT:
1425 hotkey << wxT("KP_Right" );
1426 break;
1427 case WXK_NUMPAD_DOWN:
1428 hotkey << wxT("KP_Down" );
1429 break;
5bd24f72 1430 case WXK_NUMPAD_PAGEUP:
f005ea42 1431 hotkey << wxT("KP_Page_Up" );
bbcd4085 1432 break;
5bd24f72 1433 case WXK_NUMPAD_PAGEDOWN:
f005ea42 1434 hotkey << wxT("KP_Page_Down" );
bbcd4085
RR
1435 break;
1436 case WXK_NUMPAD_END:
1437 hotkey << wxT("KP_End" );
1438 break;
1439 case WXK_NUMPAD_BEGIN:
1440 hotkey << wxT("KP_Begin" );
1441 break;
1442 case WXK_NUMPAD_EQUAL:
1443 hotkey << wxT("KP_Equal" );
1444 break;
1445 case WXK_NUMPAD_MULTIPLY:
1446 hotkey << wxT("KP_Multiply" );
1447 break;
1448 case WXK_NUMPAD_ADD:
1449 hotkey << wxT("KP_Add" );
1450 break;
1451 case WXK_NUMPAD_SEPARATOR:
1452 hotkey << wxT("KP_Separator" );
1453 break;
1454 case WXK_NUMPAD_SUBTRACT:
1455 hotkey << wxT("KP_Subtract" );
1456 break;
1457 case WXK_NUMPAD_DECIMAL:
1458 hotkey << wxT("KP_Decimal" );
1459 break;
1460 case WXK_NUMPAD_DIVIDE:
1461 hotkey << wxT("KP_Divide" );
1462 break;
1463 case WXK_NUMPAD0: case WXK_NUMPAD1: case WXK_NUMPAD2:
1464 case WXK_NUMPAD3: case WXK_NUMPAD4: case WXK_NUMPAD5:
1465 case WXK_NUMPAD6: case WXK_NUMPAD7: case WXK_NUMPAD8: case WXK_NUMPAD9:
1466 hotkey += wxString::Format(wxT("KP_%d"), code - WXK_NUMPAD0);
1467 break;
1468 case WXK_WINDOWS_LEFT:
1469 hotkey << wxT("Super_L" );
1470 break;
1471 case WXK_WINDOWS_RIGHT:
1472 hotkey << wxT("Super_R" );
1473 break;
1474 case WXK_WINDOWS_MENU:
1475 hotkey << wxT("Menu" );
1476 break;
1477 case WXK_COMMAND:
1478 hotkey << wxT("Command" );
1479 break;
1480 /* These probably wouldn't work as there is no SpecialX in gdk/keynames.txt
88d19775
MR
1481 case WXK_SPECIAL1: case WXK_SPECIAL2: case WXK_SPECIAL3: case WXK_SPECIAL4:
1482 case WXK_SPECIAL5: case WXK_SPECIAL6: case WXK_SPECIAL7: case WXK_SPECIAL8:
1483 case WXK_SPECIAL9: case WXK_SPECIAL10: case WXK_SPECIAL11: case WXK_SPECIAL12:
1484 case WXK_SPECIAL13: case WXK_SPECIAL14: case WXK_SPECIAL15: case WXK_SPECIAL16:
bbcd4085
RR
1485 case WXK_SPECIAL17: case WXK_SPECIAL18: case WXK_SPECIAL19: case WXK_SPECIAL20:
1486 hotkey += wxString::Format(wxT("Special%d"), code - WXK_SPECIAL1 + 1);
1487 break;
1488 */
90527a50 1489 // if there are any other keys wxAcceleratorEntry::Create() may
a070d8ce 1490 // return, we should process them here
8bbe427f 1491
717a57c2 1492 default:
a070d8ce 1493 if ( code < 127 )
717a57c2 1494 {
30083ad8
VZ
1495 const wxString
1496 name = wxGTK_CONV_BACK_SYS(gdk_keyval_name((guint)code));
1497 if ( !name.empty() )
a070d8ce
VZ
1498 {
1499 hotkey << name;
1500 break;
1501 }
717a57c2 1502 }
c801d85f 1503
717a57c2
VZ
1504 wxFAIL_MSG( wxT("unknown keyboard accel") );
1505 }
c801d85f 1506
717a57c2 1507 delete accel;
631f1bfe 1508 }
717a57c2
VZ
1509
1510 return hotkey;
631f1bfe 1511}
a070d8ce 1512
717a57c2 1513#endif // wxUSE_ACCEL
43a11e2a 1514
bcf881ef
JS
1515const char *wxGetStockGtkID(wxWindowID id)
1516{
1517 #define STOCKITEM(wx,gtk) \
1518 case wx: \
1519 return gtk;
1520
1521 #define STOCKITEM_MISSING(wx) \
1522 case wx: \
1523 return NULL;
1524
1525 #if GTK_CHECK_VERSION(2,4,0)
1526 #define STOCKITEM_24(wx,gtk) STOCKITEM(wx,gtk)
1527 #else
1528 #define STOCKITEM_24(wx,gtk) STOCKITEM_MISSING(wx)
1529 #endif
1530
1531 #if GTK_CHECK_VERSION(2,6,0)
1532 #define STOCKITEM_26(wx,gtk) STOCKITEM(wx,gtk)
1533 #else
1534 #define STOCKITEM_26(wx,gtk) STOCKITEM_MISSING(wx)
1535 #endif
1536
1537 #if GTK_CHECK_VERSION(2,10,0)
1538 #define STOCKITEM_210(wx,gtk) STOCKITEM(wx,gtk)
1539 #else
1540 #define STOCKITEM_210(wx,gtk) STOCKITEM_MISSING(wx)
1541 #endif
1542
1543
1544 switch (id)
1545 {
1546 STOCKITEM_26(wxID_ABOUT, GTK_STOCK_ABOUT)
1547 STOCKITEM(wxID_ADD, GTK_STOCK_ADD)
1548 STOCKITEM(wxID_APPLY, GTK_STOCK_APPLY)
1549 STOCKITEM(wxID_BOLD, GTK_STOCK_BOLD)
1550 STOCKITEM(wxID_CANCEL, GTK_STOCK_CANCEL)
1551 STOCKITEM(wxID_CLEAR, GTK_STOCK_CLEAR)
1552 STOCKITEM(wxID_CLOSE, GTK_STOCK_CLOSE)
1553 STOCKITEM(wxID_COPY, GTK_STOCK_COPY)
1554 STOCKITEM(wxID_CUT, GTK_STOCK_CUT)
1555 STOCKITEM(wxID_DELETE, GTK_STOCK_DELETE)
1556 STOCKITEM_26(wxID_EDIT, GTK_STOCK_EDIT)
1557 STOCKITEM(wxID_FIND, GTK_STOCK_FIND)
1558 STOCKITEM_26(wxID_FILE, GTK_STOCK_FILE)
1559 STOCKITEM(wxID_REPLACE, GTK_STOCK_FIND_AND_REPLACE)
1560 STOCKITEM(wxID_BACKWARD, GTK_STOCK_GO_BACK)
1561 STOCKITEM(wxID_DOWN, GTK_STOCK_GO_DOWN)
1562 STOCKITEM(wxID_FORWARD, GTK_STOCK_GO_FORWARD)
1563 STOCKITEM(wxID_UP, GTK_STOCK_GO_UP)
1564 STOCKITEM(wxID_HELP, GTK_STOCK_HELP)
1565 STOCKITEM(wxID_HOME, GTK_STOCK_HOME)
1566 STOCKITEM_24(wxID_INDENT, GTK_STOCK_INDENT)
1567 STOCKITEM(wxID_INDEX, GTK_STOCK_INDEX)
1568 STOCKITEM(wxID_ITALIC, GTK_STOCK_ITALIC)
1569 STOCKITEM(wxID_JUSTIFY_CENTER, GTK_STOCK_JUSTIFY_CENTER)
1570 STOCKITEM(wxID_JUSTIFY_FILL, GTK_STOCK_JUSTIFY_FILL)
1571 STOCKITEM(wxID_JUSTIFY_LEFT, GTK_STOCK_JUSTIFY_LEFT)
1572 STOCKITEM(wxID_JUSTIFY_RIGHT, GTK_STOCK_JUSTIFY_RIGHT)
1573 STOCKITEM(wxID_NEW, GTK_STOCK_NEW)
1574 STOCKITEM(wxID_NO, GTK_STOCK_NO)
1575 STOCKITEM(wxID_OK, GTK_STOCK_OK)
1576 STOCKITEM(wxID_OPEN, GTK_STOCK_OPEN)
1577 STOCKITEM(wxID_PASTE, GTK_STOCK_PASTE)
1578 STOCKITEM(wxID_PREFERENCES, GTK_STOCK_PREFERENCES)
1579 STOCKITEM(wxID_PRINT, GTK_STOCK_PRINT)
1580 STOCKITEM(wxID_PREVIEW, GTK_STOCK_PRINT_PREVIEW)
1581 STOCKITEM(wxID_PROPERTIES, GTK_STOCK_PROPERTIES)
1582 STOCKITEM(wxID_EXIT, GTK_STOCK_QUIT)
1583 STOCKITEM(wxID_REDO, GTK_STOCK_REDO)
1584 STOCKITEM(wxID_REFRESH, GTK_STOCK_REFRESH)
1585 STOCKITEM(wxID_REMOVE, GTK_STOCK_REMOVE)
1586 STOCKITEM(wxID_REVERT_TO_SAVED, GTK_STOCK_REVERT_TO_SAVED)
1587 STOCKITEM(wxID_SAVE, GTK_STOCK_SAVE)
1588 STOCKITEM(wxID_SAVEAS, GTK_STOCK_SAVE_AS)
1589 STOCKITEM_210(wxID_SELECTALL, GTK_STOCK_SELECT_ALL)
1590 STOCKITEM(wxID_STOP, GTK_STOCK_STOP)
1591 STOCKITEM(wxID_UNDELETE, GTK_STOCK_UNDELETE)
1592 STOCKITEM(wxID_UNDERLINE, GTK_STOCK_UNDERLINE)
1593 STOCKITEM(wxID_UNDO, GTK_STOCK_UNDO)
1594 STOCKITEM_24(wxID_UNINDENT, GTK_STOCK_UNINDENT)
1595 STOCKITEM(wxID_YES, GTK_STOCK_YES)
1596 STOCKITEM(wxID_ZOOM_100, GTK_STOCK_ZOOM_100)
1597 STOCKITEM(wxID_ZOOM_FIT, GTK_STOCK_ZOOM_FIT)
1598 STOCKITEM(wxID_ZOOM_IN, GTK_STOCK_ZOOM_IN)
1599 STOCKITEM(wxID_ZOOM_OUT, GTK_STOCK_ZOOM_OUT)
1600
1601 default:
1602 wxFAIL_MSG( _T("invalid stock item ID") );
1603 break;
1604 };
1605
1606 #undef STOCKITEM
1607
1608 return NULL;
1609}
1610
19abd352
PC
1611#if wxUSE_ACCEL
1612static
bcf881ef
JS
1613bool wxGetStockGtkAccelerator(const char *id, GdkModifierType *mod, guint *key)
1614{
1615 if (!id)
1616 return false;
1617
1618 GtkStockItem stock_item;
1619 if (gtk_stock_lookup (id, &stock_item))
1620 {
1621 if (key) *key = stock_item.keyval;
1622 if (mod) *mod = stock_item.modifier;
1623
1624 // some GTK stock items have zero values for the keyval;
1625 // it means that they do not have an accelerator...
1626 if (stock_item.keyval)
1627 return true;
1628 }
1629
1630 return false;
1631}
19abd352 1632#endif // wxUSE_ACCEL
bcf881ef 1633
28fcfbfe 1634#endif // wxUSE_MENUS