]> git.saurik.com Git - wxWidgets.git/blame - src/gtk/menu.cpp
Improve wxMenu docs
[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
610wxMenuItem::wxMenuItem(wxMenu *parentMenu,
611 int id,
612 const wxString& text,
613 const wxString& help,
614 bool isCheckable,
615 wxMenu *subMenu)
616 : wxMenuItemBase(parentMenu, id, text, help,
617 isCheckable ? wxITEM_CHECK : wxITEM_NORMAL, subMenu)
618{
092f7536 619 Init(text);
2368dcda
VZ
620}
621
092f7536 622void wxMenuItem::Init(const wxString& text)
c801d85f 623{
37d403aa 624 m_labelWidget = (GtkWidget *) NULL;
83624f79 625 m_menuItem = (GtkWidget *) NULL;
974e8d94 626
092f7536 627 DoSetText(text);
6de97a3b 628}
c801d85f 629
d1b15f03
RR
630wxMenuItem::~wxMenuItem()
631{
632 // don't delete menu items, the menus take care of that
633}
634
717a57c2 635// return the menu item text without any menu accels
3b59cdbf 636/* static */
52af3158 637
52af3158 638wxString wxMenuItemBase::GetLabelText(const wxString& text)
717a57c2 639{
52af3158
JS
640 // The argument to this function will now always be in wxWidgets standard label
641 // format, not GTK+ format, so we do what the other ports do.
642
643 return wxStripMenuCodes(text);
644
645#if 0
717a57c2 646 wxString label;
2368dcda 647
3b59cdbf 648 for ( const wxChar *pc = text.c_str(); *pc; pc++ )
717a57c2 649 {
f0b72e0d
RR
650 if ( *pc == wxT('\t'))
651 break;
9959e2b6 652
7cf4f7e2 653 if ( *pc == wxT('_') )
717a57c2 654 {
2b5f62a0 655 // GTK 1.2 escapes "xxx_xxx" to "xxx__xxx"
d76419bd
RR
656 pc++;
657 label += *pc;
658 continue;
659 }
2368dcda 660
2b5f62a0 661 if ( *pc == wxT('\\') )
d76419bd 662 {
2b5f62a0
VZ
663 // GTK 2.0 escapes "xxx/xxx" to "xxx\/xxx"
664 pc++;
665 label += *pc;
717a57c2
VZ
666 continue;
667 }
668
7cf4f7e2
GD
669 if ( (*pc == wxT('&')) && (*(pc+1) != wxT('&')) )
670 {
671 // wxMSW escapes "&"
672 // "&" is doubled to indicate "&" instead of accelerator
673 continue;
674 }
a01fe3d6 675
717a57c2
VZ
676 label += *pc;
677 }
a01fe3d6 678
52af3158 679 // wxPrintf( wxT("GetLabelText(): text %s label %s\n"), text.c_str(), label.c_str() );
d9e403cc 680
717a57c2 681 return label;
52af3158 682#endif
717a57c2
VZ
683}
684
52af3158
JS
685wxString wxMenuItem::GetItemLabel() const
686{
b1f17bf0 687 wxString label = wxConvertMnemonicsFromGTK(m_text);
a738f87c 688 if (!m_hotKey.IsEmpty())
b1f17bf0 689 label << "\t" << m_hotKey;
a738f87c 690 return label;
52af3158
JS
691}
692
693void wxMenuItem::SetItemLabel( const wxString& str )
717a57c2 694{
ee0a94cf
RR
695 // cache some data which must be used later
696 bool isstock = wxIsStockID(GetId());
697 const char *stockid = NULL;
698 if (isstock)
699 stockid = wxGetStockGtkID(GetId());
700
5869f93f
JS
701 // Some optimization to avoid flicker
702 wxString oldLabel = m_text;
98f29783 703 oldLabel = wxStripMenuCodes(oldLabel);
5869f93f 704 oldLabel.Replace(wxT("_"), wxT(""));
98f29783 705 wxString label1 = wxStripMenuCodes(str);
6c309653 706#if wxUSE_ACCEL
a8e607d9 707 wxString oldhotkey = GetHotKey(); // Store the old hotkey in Ctrl-foo format
5f11fef5 708 wxCharBuffer oldbuf = wxGTK_CONV_SYS( GetGtkHotKey(*this) ); // and as <control>foo
6c309653 709#endif // wxUSE_ACCEL
a01fe3d6 710
717a57c2 711 DoSetText(str);
354aa1e3 712
6c309653 713#if wxUSE_ACCEL
88d19775 714 if (oldLabel == label1 &&
ee0a94cf 715 oldhotkey == GetHotKey()) // Make sure we can change a hotkey even if the label is unaltered
98f29783
RR
716 return;
717
354aa1e3
RR
718 if (m_menuItem)
719 {
37d403aa
JS
720 GtkLabel *label;
721 if (m_labelWidget)
2b5f62a0 722 label = (GtkLabel*) m_labelWidget;
37d403aa 723 else
2b5f62a0 724 label = GTK_LABEL( GTK_BIN(m_menuItem)->child );
717a57c2 725
ee0a94cf
RR
726 // stock menu items can have empty labels:
727 wxString text = m_text;
728 if (text.IsEmpty() && !IsSeparator())
729 {
730 wxASSERT_MSG(isstock, wxT("A non-stock menu item with an empty label?"));
731 text = wxGetStockLabel(GetId());
732
733 // need & => _ conversion
734 text = GTKProcessMenuItemLabel(text, NULL);
735 }
736
737 gtk_label_set_text_with_mnemonic( GTK_LABEL(label), wxGTK_CONV_SYS(text) );
354aa1e3 738 }
98f29783 739
ee0a94cf 740 // remove old accelerator from our parent's accelerator group, if present
98f29783
RR
741 guint accel_key;
742 GdkModifierType accel_mods;
ee0a94cf 743 if (oldbuf[(size_t)0] != '\0')
98f29783 744 {
ee0a94cf
RR
745 gtk_accelerator_parse( (const char*) oldbuf, &accel_key, &accel_mods);
746 if (accel_key != 0)
747 {
10bd1f7d 748 gtk_widget_remove_accelerator(m_menuItem,
ee0a94cf
RR
749 m_parentMenu->m_accel,
750 accel_key,
751 accel_mods );
752 }
753 }
754 else if (isstock)
755 {
756 // if the accelerator was taken from a stock ID, just get it back from GTK+ stock
757 if (wxGetStockGtkAccelerator(stockid, &accel_mods, &accel_key))
10bd1f7d 758 gtk_widget_remove_accelerator( m_menuItem,
ee0a94cf
RR
759 m_parentMenu->m_accel,
760 accel_key,
761 accel_mods );
98f29783
RR
762 }
763
ee0a94cf 764 // add new accelerator to our parent's accelerator group
5f11fef5 765 wxCharBuffer buf = wxGTK_CONV_SYS( GetGtkHotKey(*this) );
ee0a94cf 766 if (buf[(size_t)0] != '\0')
98f29783 767 {
ee0a94cf
RR
768 gtk_accelerator_parse( (const char*) buf, &accel_key, &accel_mods);
769 if (accel_key != 0)
770 {
10bd1f7d 771 gtk_widget_add_accelerator( m_menuItem,
ee0a94cf
RR
772 "activate",
773 m_parentMenu->m_accel,
774 accel_key,
775 accel_mods,
776 GTK_ACCEL_VISIBLE);
777 }
778 }
779 else if (isstock)
780 {
781 // if the accelerator was taken from a stock ID, just get it back from GTK+ stock
782 if (wxGetStockGtkAccelerator(stockid, &accel_mods, &accel_key))
10bd1f7d 783 gtk_widget_remove_accelerator( m_menuItem,
ee0a94cf
RR
784 m_parentMenu->m_accel,
785 accel_key,
786 accel_mods );
98f29783 787 }
19abd352 788#endif // wxUSE_ACCEL
354aa1e3
RR
789}
790
ee0a94cf 791// NOTE: this function is different from the similar functions GTKProcessMnemonics()
52af3158 792// implemented in control.cpp and from wxMenuItemBase::GetLabelText...
ee0a94cf
RR
793// so there's no real code duplication
794wxString wxMenuItem::GTKProcessMenuItemLabel(const wxString& str, wxString *hotKey)
716b7364 795{
ee0a94cf
RR
796 wxString text;
797
2b5f62a0 798 // '\t' is the deliminator indicating a hot key
e0a050e3
VS
799 wxString::const_iterator pc = str.begin();
800 while ( pc != str.end() && *pc != wxT('\t') )
83624f79 801 {
e0a050e3 802 if (*pc == wxT('&'))
23280650 803 {
e0a050e3
VS
804 wxString::const_iterator next = pc + 1;
805 if (next != str.end() && *next == wxT('&'))
806 {
807 // "&" is doubled to indicate "&" instead of accelerator
808 ++pc;
809 text << wxT('&');
810 }
811 else
812 {
813 text << wxT('_');
814 }
572d7461 815 }
2b5f62a0
VZ
816 else if ( *pc == wxT('_') ) // escape underscores
817 {
ee0a94cf 818 text << wxT("__");
2b5f62a0 819 }
9959e2b6 820 else
23280650 821 {
ee0a94cf 822 text << *pc;
2b5f62a0
VZ
823 }
824 ++pc;
83624f79 825 }
a01fe3d6 826
ee0a94cf 827 if (hotKey)
d7dbc98a 828 {
ee0a94cf
RR
829 hotKey->Empty();
830 if(*pc == wxT('\t'))
831 {
832 pc++;
e0a050e3 833 hotKey->assign(pc, str.end());
ee0a94cf 834 }
d7dbc98a 835 }
88d19775 836
ee0a94cf
RR
837 return text;
838}
839
840// it's valid for this function to be called even if m_menuItem == NULL
841void wxMenuItem::DoSetText( const wxString& str )
842{
843 m_text.Empty();
844 m_text = GTKProcessMenuItemLabel(str, &m_hotKey);
716b7364
RR
845}
846
717a57c2
VZ
847#if wxUSE_ACCEL
848
849wxAcceleratorEntry *wxMenuItem::GetAccel() const
850{
1987af7e 851 if ( !GetHotKey() )
717a57c2
VZ
852 {
853 // nothing
90527a50 854 return NULL;
717a57c2
VZ
855 }
856
90527a50
VZ
857 // accelerator parsing code looks for them after a TAB, so insert a dummy
858 // one here
717a57c2 859 wxString label;
1987af7e 860 label << wxT('\t') << GetHotKey();
717a57c2 861
90527a50 862 return wxAcceleratorEntry::Create(label);
717a57c2
VZ
863}
864
865#endif // wxUSE_ACCEL
866
96fd301f 867void wxMenuItem::Check( bool check )
716b7364 868{
223d09f6 869 wxCHECK_RET( m_menuItem, wxT("invalid menu item") );
db1b4961 870
974e8d94
VZ
871 if (check == m_isChecked)
872 return;
2d17d68f 873
974e8d94 874 wxMenuItemBase::Check( check );
0472ece7 875
24bcaec3 876 switch ( GetKind() )
0472ece7 877 {
24bcaec3
VZ
878 case wxITEM_CHECK:
879 case wxITEM_RADIO:
8394218c 880 gtk_check_menu_item_set_active( (GtkCheckMenuItem*)m_menuItem, (gint)check );
24bcaec3
VZ
881 break;
882
883 default:
884 wxFAIL_MSG( _T("can't check this item") );
0472ece7 885 }
716b7364
RR
886}
887
8bbe427f
VZ
888void wxMenuItem::Enable( bool enable )
889{
223d09f6 890 wxCHECK_RET( m_menuItem, wxT("invalid menu item") );
db1b4961 891
83624f79 892 gtk_widget_set_sensitive( m_menuItem, enable );
974e8d94 893 wxMenuItemBase::Enable( enable );
a9c96bcc
RR
894}
895
96fd301f 896bool wxMenuItem::IsChecked() const
716b7364 897{
670f9935 898 wxCHECK_MSG( m_menuItem, false, wxT("invalid menu item") );
db1b4961 899
670f9935 900 wxCHECK_MSG( IsCheckable(), false,
974e8d94 901 wxT("can't get state of uncheckable item!") );
96fd301f 902
974e8d94 903 return ((GtkCheckMenuItem*)m_menuItem)->active != 0;
716b7364
RR
904}
905
db1b4961 906//-----------------------------------------------------------------------------
83624f79 907// wxMenu
db1b4961
RR
908//-----------------------------------------------------------------------------
909
3ed946f2
PC
910extern "C" {
911// "map" from m_menu
912static void menu_map(GtkWidget*, wxMenu* menu)
913{
914 wxMenuEvent event(wxEVT_MENU_OPEN, menu->m_popupShown ? -1 : 0, menu);
915 DoCommonMenuCallbackCode(menu, event);
916}
917
918// "hide" from m_menu
919static void menu_hide(GtkWidget*, wxMenu* menu)
920{
921 wxMenuEvent event(wxEVT_MENU_CLOSE, menu->m_popupShown ? -1 : 0, menu);
922 menu->m_popupShown = false;
923 DoCommonMenuCallbackCode(menu, event);
924}
925}
926
c801d85f
KB
927IMPLEMENT_DYNAMIC_CLASS(wxMenu,wxEvtHandler)
928
717a57c2 929void wxMenu::Init()
c801d85f 930{
3ed946f2
PC
931 m_popupShown = false;
932
034be888 933 m_accel = gtk_accel_group_new();
6d971354 934 m_menu = gtk_menu_new();
defc0789
VS
935 // NB: keep reference to the menu so that it is not destroyed behind
936 // our back by GTK+ e.g. when it is removed from menubar:
0f35e441
PC
937 g_object_ref(m_menu);
938 gtk_object_sink(GTK_OBJECT(m_menu));
8bbe427f 939
2b1c162e 940 m_owner = (GtkWidget*) NULL;
2b2edbed 941
d9e403cc
RR
942 // Tearoffs are entries, just like separators. So if we want this
943 // menu to be a tear-off one, we just append a tearoff entry
944 // immediately.
9959e2b6 945 if ( m_style & wxMENU_TEAROFF )
2b2edbed 946 {
9959e2b6 947 GtkWidget *tearoff = gtk_tearoff_menu_item_new();
6d971354 948
1ab9e06d 949 gtk_menu_shell_append(GTK_MENU_SHELL(m_menu), tearoff);
9959e2b6 950 }
6d971354 951
9959e2b6 952 m_prevRadio = NULL;
c801d85f 953
717a57c2 954 // append the title as the very first entry if we have it
9959e2b6 955 if ( !m_title.empty() )
d1b15f03 956 {
9959e2b6 957 Append(wxGTK_TITLE_ID, m_title);
717a57c2 958 AppendSeparator();
d1b15f03 959 }
3ed946f2
PC
960
961 // "show" occurs for sub-menus which are not showing, so use "map" instead
962 g_signal_connect(m_menu, "map", G_CALLBACK(menu_map), this);
963 g_signal_connect(m_menu, "hide", G_CALLBACK(menu_hide), this);
717a57c2 964}
15a2076a 965
717a57c2
VZ
966wxMenu::~wxMenu()
967{
3ed946f2
PC
968 // see wxMenu::Init
969 g_object_unref(m_menu);
52af3158 970
3ed946f2
PC
971 // if the menu is inserted in another menu at this time, there was
972 // one more reference to it:
973 if (m_owner)
974 gtk_widget_destroy(m_menu);
f0378929 975
3ed946f2 976 g_object_unref(m_accel);
c2dd8380
GL
977}
978
978af864
VZ
979void wxMenu::SetLayoutDirection(const wxLayoutDirection dir)
980{
981 if ( m_owner )
982 wxWindow::GTKSetLayout(m_owner, dir);
983 //else: will be called later by wxMenuBar again
984}
985
986wxLayoutDirection wxMenu::GetLayoutDirection() const
987{
988 return wxWindow::GTKGetLayout(m_owner);
989}
990
49826dab 991bool wxMenu::GtkAppend(wxMenuItem *mitem, int pos)
c2dd8380 992{
717a57c2 993 GtkWidget *menuItem;
96fd301f 994
ee0a94cf 995 // cache some data used later
84c51319 996 wxString text = mitem->wxMenuItemBase::GetItemLabel();
ee0a94cf
RR
997 int id = mitem->GetId();
998 bool isstock = wxIsStockID(id);
999 const char *stockid = NULL;
1000 if (isstock)
1001 stockid = wxGetStockGtkID(mitem->GetId());
1002
1003 // stock menu items can have an empty label
1004 if (text.IsEmpty() && !mitem->IsSeparator())
1005 {
1006 wxASSERT_MSG(isstock, wxT("A non-stock menu item with an empty label?"));
1007 text = wxGetStockLabel(id);
1008
1009 // need & => _ conversion
1010 text = wxMenuItem::GTKProcessMenuItemLabel(text, NULL);
1011 }
e31126cb 1012
717a57c2
VZ
1013 if ( mitem->IsSeparator() )
1014 {
6d971354 1015 menuItem = gtk_separator_menu_item_new();
45813bad 1016 m_prevRadio = NULL;
837904f2 1017 }
ab73fe8d 1018 else if ( mitem->GetBitmap().Ok() ||
ee0a94cf 1019 (mitem->GetKind() == wxITEM_NORMAL && isstock) )
37d403aa 1020 {
ab73fe8d 1021 wxBitmap bitmap(mitem->GetBitmap());
9959e2b6 1022
5f11fef5 1023 menuItem = gtk_image_menu_item_new_with_mnemonic( wxGTK_CONV_SYS( text ) );
9959e2b6 1024
b1ad1424 1025 GtkWidget *image;
ab73fe8d 1026 if ( !bitmap.Ok() )
b1ad1424 1027 {
ab73fe8d
VZ
1028 // use stock bitmap for this item if available on the assumption
1029 // that it never hurts to follow GTK+ conventions more closely
ee0a94cf
RR
1030 image = stockid ? gtk_image_new_from_stock(stockid, GTK_ICON_SIZE_MENU)
1031 : NULL;
b1ad1424 1032 }
ab73fe8d 1033 else // we have a custom bitmap
b1ad1424 1034 {
ab73fe8d
VZ
1035 wxASSERT_MSG( mitem->GetKind() == wxITEM_NORMAL,
1036 _T("only normal menu items can have bitmaps") );
1037
1038 if ( bitmap.HasPixbuf() )
1039 {
1040 image = gtk_image_new_from_pixbuf(bitmap.GetPixbuf());
1041 }
1042 else
1043 {
1044 GdkPixmap *gdk_pixmap = bitmap.GetPixmap();
1045 GdkBitmap *gdk_bitmap = bitmap.GetMask() ?
1046 bitmap.GetMask()->GetBitmap() :
1047 (GdkBitmap*) NULL;
1048 image = gtk_image_new_from_pixmap( gdk_pixmap, gdk_bitmap );
1049 }
b1ad1424 1050 }
88d19775 1051
ab73fe8d
VZ
1052 if ( image )
1053 {
1054 gtk_widget_show(image);
9959e2b6 1055
ab73fe8d
VZ
1056 gtk_image_menu_item_set_image( GTK_IMAGE_MENU_ITEM(menuItem), image );
1057 }
9959e2b6 1058
6d971354
RR
1059 m_prevRadio = NULL;
1060 }
717a57c2
VZ
1061 else // a normal item
1062 {
52af3158 1063 // NB: 'text' variable has "_" instead of "&" after mitem->SetItemLabel()
ee0a94cf 1064 // so don't use it
717a57c2 1065
d65c269b
VZ
1066 switch ( mitem->GetKind() )
1067 {
546bfbea 1068 case wxITEM_CHECK:
6d971354 1069 {
5f11fef5 1070 menuItem = gtk_check_menu_item_new_with_mnemonic( wxGTK_CONV_SYS( text ) );
6d971354 1071 m_prevRadio = NULL;
d65c269b 1072 break;
6d971354 1073 }
d65c269b 1074
546bfbea 1075 case wxITEM_RADIO:
6d971354
RR
1076 {
1077 GSList *group = NULL;
1078 if ( m_prevRadio == NULL )
d65c269b
VZ
1079 {
1080 // start of a new radio group
5f11fef5
VZ
1081 m_prevRadio = menuItem =
1082 gtk_radio_menu_item_new_with_mnemonic( group, wxGTK_CONV_SYS( text ) );
d65c269b
VZ
1083 }
1084 else // continue the radio group
1085 {
6d971354 1086 group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (m_prevRadio));
5f11fef5
VZ
1087 m_prevRadio = menuItem =
1088 gtk_radio_menu_item_new_with_mnemonic( group, wxGTK_CONV_SYS( text ) );
d65c269b 1089 }
c0d0a552 1090 break;
6d971354 1091 }
d65c269b
VZ
1092
1093 default:
1094 wxFAIL_MSG( _T("unexpected menu item kind") );
1095 // fall through
1096
546bfbea 1097 case wxITEM_NORMAL:
6d971354 1098 {
5f11fef5 1099 menuItem = gtk_menu_item_new_with_mnemonic( wxGTK_CONV_SYS( text ) );
6d971354 1100 m_prevRadio = NULL;
d65c269b 1101 break;
6d971354 1102 }
d65c269b
VZ
1103 }
1104
6d971354 1105 }
9959e2b6 1106
6c309653 1107#if wxUSE_ACCEL
6d971354
RR
1108 guint accel_key;
1109 GdkModifierType accel_mods;
5f11fef5 1110 wxCharBuffer buf = wxGTK_CONV_SYS( GetGtkHotKey(*mitem) );
9959e2b6 1111
ee0a94cf
RR
1112 if (buf[(size_t)0] != '\0')
1113 {
1114 gtk_accelerator_parse( (const char*) buf, &accel_key, &accel_mods);
1115 if (accel_key != 0)
1116 {
10bd1f7d 1117 gtk_widget_add_accelerator (menuItem,
ee0a94cf
RR
1118 "activate",
1119 m_accel,
1120 accel_key,
1121 accel_mods,
1122 GTK_ACCEL_VISIBLE);
1123 }
1124 }
1125 else if (isstock)
6d971354 1126 {
ee0a94cf
RR
1127 // if the accelerator was taken from a stock ID, just get it back from GTK+ stock
1128 if (wxGetStockGtkAccelerator(stockid, &accel_mods, &accel_key))
10bd1f7d 1129 gtk_widget_add_accelerator( menuItem,
ee0a94cf
RR
1130 "activate",
1131 m_accel,
1132 accel_key,
1133 accel_mods,
1134 GTK_ACCEL_VISIBLE);
717a57c2 1135 }
19abd352 1136#endif // wxUSE_ACCEL
9959e2b6 1137
e31126cb
RR
1138 if (pos == -1)
1139 gtk_menu_shell_append(GTK_MENU_SHELL(m_menu), menuItem);
1140 else
1141 gtk_menu_shell_insert(GTK_MENU_SHELL(m_menu), menuItem, pos);
1142
6d971354 1143 gtk_widget_show( menuItem );
23280650 1144
717a57c2
VZ
1145 if ( !mitem->IsSeparator() )
1146 {
2b5f62a0 1147 wxASSERT_MSG( menuItem, wxT("invalid menuitem") );
a01fe3d6 1148
9fa72bd2
MR
1149 g_signal_connect (menuItem, "select",
1150 G_CALLBACK (gtk_menu_hilight_callback), this);
1151 g_signal_connect (menuItem, "deselect",
1152 G_CALLBACK (gtk_menu_nolight_callback), this);
e31126cb 1153
88d19775
MR
1154 if ( mitem->IsSubMenu() && mitem->GetKind() != wxITEM_RADIO && mitem->GetKind() != wxITEM_CHECK )
1155 {
e31126cb
RR
1156 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menuItem), mitem->GetSubMenu()->m_menu );
1157
1158 gtk_widget_show( mitem->GetSubMenu()->m_menu );
1159
1160 // if adding a submenu to a menu already existing in the menu bar, we
1161 // must set invoking window to allow processing events from this
1162 // submenu
1163 if ( m_invokingWindow )
1164 wxMenubarSetInvokingWindow(mitem->GetSubMenu(), m_invokingWindow);
88d19775
MR
1165 }
1166 else
1167 {
9fa72bd2
MR
1168 g_signal_connect (menuItem, "activate",
1169 G_CALLBACK (gtk_menu_clicked_callback),
1170 this);
88d19775 1171 }
717a57c2 1172 }
23280650 1173
837904f2 1174 mitem->SetMenuItem(menuItem);
837904f2 1175
6d971354 1176 if (ms_locked)
d65c269b 1177 {
6d971354
RR
1178 // This doesn't even exist!
1179 // gtk_widget_lock_accelerators(mitem->GetMenuItem());
d65c269b 1180 }
d65c269b 1181
670f9935 1182 return true;
6de97a3b 1183}
c801d85f 1184
9add9367 1185wxMenuItem* wxMenu::DoAppend(wxMenuItem *mitem)
828f655f 1186{
9add9367
RD
1187 if (!GtkAppend(mitem))
1188 return NULL;
9959e2b6 1189
9add9367 1190 return wxMenuBase::DoAppend(mitem);
c33c4050
RR
1191}
1192
9add9367 1193wxMenuItem* wxMenu::DoInsert(size_t pos, wxMenuItem *item)
c33c4050 1194{
717a57c2 1195 if ( !wxMenuBase::DoInsert(pos, item) )
9add9367 1196 return NULL;
c626a8b7 1197
6d971354 1198 // TODO
49826dab 1199 if ( !GtkAppend(item, (int)pos) )
9add9367 1200 return NULL;
32db328c 1201
9add9367 1202 return item;
c33c4050
RR
1203}
1204
717a57c2 1205wxMenuItem *wxMenu::DoRemove(wxMenuItem *item)
c33c4050 1206{
717a57c2 1207 if ( !wxMenuBase::DoRemove(item) )
8d749001
VZ
1208 return NULL;
1209
1210 GtkWidget * const mitem = item->GetMenuItem();
1211 if ( m_prevRadio == mitem )
1212 {
1213 // deleting an item starts a new radio group (has to as we shouldn't
1214 // keep a deleted pointer anyhow)
1215 m_prevRadio = NULL;
1216 }
c626a8b7 1217
717a57c2
VZ
1218 // TODO: this code doesn't delete the item factory item and this seems
1219 // impossible as of GTK 1.2.6.
8d749001 1220 gtk_widget_destroy( mitem );
c626a8b7 1221
717a57c2 1222 return item;
c33c4050
RR
1223}
1224
96fd301f
VZ
1225int wxMenu::FindMenuIdByMenuItem( GtkWidget *menuItem ) const
1226{
222ed1d6 1227 wxMenuItemList::compatibility_iterator node = m_items.GetFirst();
83624f79
RR
1228 while (node)
1229 {
b1d4dd7a 1230 wxMenuItem *item = node->GetData();
83624f79 1231 if (item->GetMenuItem() == menuItem)
88d19775 1232 return item->GetId();
b1d4dd7a 1233 node = node->GetNext();
83624f79 1234 }
96fd301f 1235
c626a8b7 1236 return wxNOT_FOUND;
6de97a3b 1237}
c801d85f 1238
978af864
VZ
1239void wxMenu::Attach(wxMenuBarBase *menubar)
1240{
1241 wxMenuBase::Attach(menubar);
1242
1243 // inherit layout direction from menubar.
1244 SetLayoutDirection(menubar->GetLayoutDirection());
1245}
1246
717a57c2
VZ
1247// ----------------------------------------------------------------------------
1248// helpers
1249// ----------------------------------------------------------------------------
1250
6d971354 1251#if wxUSE_ACCEL
a070d8ce 1252
98f29783 1253static wxString GetGtkHotKey( const wxMenuItem& item )
c801d85f 1254{
717a57c2
VZ
1255 wxString hotkey;
1256
1257 wxAcceleratorEntry *accel = item.GetAccel();
1258 if ( accel )
83624f79 1259 {
717a57c2
VZ
1260 int flags = accel->GetFlags();
1261 if ( flags & wxACCEL_ALT )
1262 hotkey += wxT("<alt>");
1263 if ( flags & wxACCEL_CTRL )
1264 hotkey += wxT("<control>");
1265 if ( flags & wxACCEL_SHIFT )
1266 hotkey += wxT("<shift>");
1267
1268 int code = accel->GetKeyCode();
1269 switch ( code )
c626a8b7 1270 {
717a57c2
VZ
1271 case WXK_F1:
1272 case WXK_F2:
1273 case WXK_F3:
1274 case WXK_F4:
1275 case WXK_F5:
1276 case WXK_F6:
1277 case WXK_F7:
1278 case WXK_F8:
1279 case WXK_F9:
1280 case WXK_F10:
1281 case WXK_F11:
1282 case WXK_F12:
bbcd4085
RR
1283 case WXK_F13:
1284 case WXK_F14:
1285 case WXK_F15:
1286 case WXK_F16:
1287 case WXK_F17:
1288 case WXK_F18:
1289 case WXK_F19:
1290 case WXK_F20:
1291 case WXK_F21:
1292 case WXK_F22:
1293 case WXK_F23:
1294 case WXK_F24:
04cc1e93 1295 hotkey += wxString::Format(wxT("F%d"), code - WXK_F1 + 1);
717a57c2 1296 break;
2368dcda 1297
a070d8ce
VZ
1298 // TODO: we should use gdk_keyval_name() (a.k.a.
1299 // XKeysymToString) here as well as hardcoding the keysym
1300 // names this might be not portable
bbcd4085 1301 case WXK_INSERT:
3ca6a5f0
BP
1302 hotkey << wxT("Insert" );
1303 break;
1304 case WXK_DELETE:
1305 hotkey << wxT("Delete" );
1306 break;
2b5f62a0
VZ
1307 case WXK_UP:
1308 hotkey << wxT("Up" );
1309 break;
1310 case WXK_DOWN:
1311 hotkey << wxT("Down" );
1312 break;
1313 case WXK_PAGEUP:
f005ea42 1314 hotkey << wxT("Page_Up" );
2b5f62a0
VZ
1315 break;
1316 case WXK_PAGEDOWN:
f005ea42 1317 hotkey << wxT("Page_Down" );
2b5f62a0
VZ
1318 break;
1319 case WXK_LEFT:
1320 hotkey << wxT("Left" );
1321 break;
1322 case WXK_RIGHT:
1323 hotkey << wxT("Right" );
1324 break;
1325 case WXK_HOME:
1326 hotkey << wxT("Home" );
1327 break;
1328 case WXK_END:
1329 hotkey << wxT("End" );
1330 break;
1331 case WXK_RETURN:
1332 hotkey << wxT("Return" );
1333 break;
bbcd4085
RR
1334 case WXK_BACK:
1335 hotkey << wxT("BackSpace" );
1336 break;
1337 case WXK_TAB:
1338 hotkey << wxT("Tab" );
1339 break;
1340 case WXK_ESCAPE:
1341 hotkey << wxT("Esc" );
1342 break;
1343 case WXK_SPACE:
1344 hotkey << wxT("space" );
1345 break;
1346 case WXK_MULTIPLY:
1347 hotkey << wxT("Multiply" );
1348 break;
1349 case WXK_ADD:
1350 hotkey << wxT("Add" );
1351 break;
1352 case WXK_SEPARATOR:
1353 hotkey << wxT("Separator" );
1354 break;
1355 case WXK_SUBTRACT:
1356 hotkey << wxT("Subtract" );
1357 break;
1358 case WXK_DECIMAL:
1359 hotkey << wxT("Decimal" );
1360 break;
1361 case WXK_DIVIDE:
1362 hotkey << wxT("Divide" );
1363 break;
1364 case WXK_CANCEL:
1365 hotkey << wxT("Cancel" );
1366 break;
1367 case WXK_CLEAR:
1368 hotkey << wxT("Clear" );
1369 break;
1370 case WXK_MENU:
1371 hotkey << wxT("Menu" );
1372 break;
1373 case WXK_PAUSE:
1374 hotkey << wxT("Pause" );
1375 break;
1376 case WXK_CAPITAL:
1377 hotkey << wxT("Capital" );
1378 break;
1379 case WXK_SELECT:
1380 hotkey << wxT("Select" );
1381 break;
1382 case WXK_PRINT:
1383 hotkey << wxT("Print" );
1384 break;
1385 case WXK_EXECUTE:
1386 hotkey << wxT("Execute" );
1387 break;
1388 case WXK_SNAPSHOT:
1389 hotkey << wxT("Snapshot" );
1390 break;
1391 case WXK_HELP:
1392 hotkey << wxT("Help" );
1393 break;
1394 case WXK_NUMLOCK:
1395 hotkey << wxT("Num_Lock" );
1396 break;
1397 case WXK_SCROLL:
1398 hotkey << wxT("Scroll_Lock" );
1399 break;
1400 case WXK_NUMPAD_INSERT:
1401 hotkey << wxT("KP_Insert" );
1402 break;
1403 case WXK_NUMPAD_DELETE:
1404 hotkey << wxT("KP_Delete" );
1405 break;
1406 case WXK_NUMPAD_SPACE:
1407 hotkey << wxT("KP_Space" );
1408 break;
1409 case WXK_NUMPAD_TAB:
1410 hotkey << wxT("KP_Tab" );
1411 break;
1412 case WXK_NUMPAD_ENTER:
1413 hotkey << wxT("KP_Enter" );
1414 break;
1415 case WXK_NUMPAD_F1: case WXK_NUMPAD_F2: case WXK_NUMPAD_F3:
1416 case WXK_NUMPAD_F4:
1417 hotkey += wxString::Format(wxT("KP_F%d"), code - WXK_NUMPAD_F1 + 1);
1418 break;
1419 case WXK_NUMPAD_HOME:
1420 hotkey << wxT("KP_Home" );
1421 break;
1422 case WXK_NUMPAD_LEFT:
1423 hotkey << wxT("KP_Left" );
1424 break;
1425 case WXK_NUMPAD_UP:
1426 hotkey << wxT("KP_Up" );
1427 break;
1428 case WXK_NUMPAD_RIGHT:
1429 hotkey << wxT("KP_Right" );
1430 break;
1431 case WXK_NUMPAD_DOWN:
1432 hotkey << wxT("KP_Down" );
1433 break;
5bd24f72 1434 case WXK_NUMPAD_PAGEUP:
f005ea42 1435 hotkey << wxT("KP_Page_Up" );
bbcd4085 1436 break;
5bd24f72 1437 case WXK_NUMPAD_PAGEDOWN:
f005ea42 1438 hotkey << wxT("KP_Page_Down" );
bbcd4085
RR
1439 break;
1440 case WXK_NUMPAD_END:
1441 hotkey << wxT("KP_End" );
1442 break;
1443 case WXK_NUMPAD_BEGIN:
1444 hotkey << wxT("KP_Begin" );
1445 break;
1446 case WXK_NUMPAD_EQUAL:
1447 hotkey << wxT("KP_Equal" );
1448 break;
1449 case WXK_NUMPAD_MULTIPLY:
1450 hotkey << wxT("KP_Multiply" );
1451 break;
1452 case WXK_NUMPAD_ADD:
1453 hotkey << wxT("KP_Add" );
1454 break;
1455 case WXK_NUMPAD_SEPARATOR:
1456 hotkey << wxT("KP_Separator" );
1457 break;
1458 case WXK_NUMPAD_SUBTRACT:
1459 hotkey << wxT("KP_Subtract" );
1460 break;
1461 case WXK_NUMPAD_DECIMAL:
1462 hotkey << wxT("KP_Decimal" );
1463 break;
1464 case WXK_NUMPAD_DIVIDE:
1465 hotkey << wxT("KP_Divide" );
1466 break;
1467 case WXK_NUMPAD0: case WXK_NUMPAD1: case WXK_NUMPAD2:
1468 case WXK_NUMPAD3: case WXK_NUMPAD4: case WXK_NUMPAD5:
1469 case WXK_NUMPAD6: case WXK_NUMPAD7: case WXK_NUMPAD8: case WXK_NUMPAD9:
1470 hotkey += wxString::Format(wxT("KP_%d"), code - WXK_NUMPAD0);
1471 break;
1472 case WXK_WINDOWS_LEFT:
1473 hotkey << wxT("Super_L" );
1474 break;
1475 case WXK_WINDOWS_RIGHT:
1476 hotkey << wxT("Super_R" );
1477 break;
1478 case WXK_WINDOWS_MENU:
1479 hotkey << wxT("Menu" );
1480 break;
1481 case WXK_COMMAND:
1482 hotkey << wxT("Command" );
1483 break;
1484 /* These probably wouldn't work as there is no SpecialX in gdk/keynames.txt
88d19775
MR
1485 case WXK_SPECIAL1: case WXK_SPECIAL2: case WXK_SPECIAL3: case WXK_SPECIAL4:
1486 case WXK_SPECIAL5: case WXK_SPECIAL6: case WXK_SPECIAL7: case WXK_SPECIAL8:
1487 case WXK_SPECIAL9: case WXK_SPECIAL10: case WXK_SPECIAL11: case WXK_SPECIAL12:
1488 case WXK_SPECIAL13: case WXK_SPECIAL14: case WXK_SPECIAL15: case WXK_SPECIAL16:
bbcd4085
RR
1489 case WXK_SPECIAL17: case WXK_SPECIAL18: case WXK_SPECIAL19: case WXK_SPECIAL20:
1490 hotkey += wxString::Format(wxT("Special%d"), code - WXK_SPECIAL1 + 1);
1491 break;
1492 */
90527a50 1493 // if there are any other keys wxAcceleratorEntry::Create() may
a070d8ce 1494 // return, we should process them here
8bbe427f 1495
717a57c2 1496 default:
a070d8ce 1497 if ( code < 127 )
717a57c2 1498 {
30083ad8
VZ
1499 const wxString
1500 name = wxGTK_CONV_BACK_SYS(gdk_keyval_name((guint)code));
1501 if ( !name.empty() )
a070d8ce
VZ
1502 {
1503 hotkey << name;
1504 break;
1505 }
717a57c2 1506 }
c801d85f 1507
717a57c2
VZ
1508 wxFAIL_MSG( wxT("unknown keyboard accel") );
1509 }
c801d85f 1510
717a57c2 1511 delete accel;
631f1bfe 1512 }
717a57c2
VZ
1513
1514 return hotkey;
631f1bfe 1515}
a070d8ce 1516
717a57c2 1517#endif // wxUSE_ACCEL
43a11e2a 1518
bcf881ef
JS
1519const char *wxGetStockGtkID(wxWindowID id)
1520{
1521 #define STOCKITEM(wx,gtk) \
1522 case wx: \
1523 return gtk;
1524
1525 #define STOCKITEM_MISSING(wx) \
1526 case wx: \
1527 return NULL;
1528
1529 #if GTK_CHECK_VERSION(2,4,0)
1530 #define STOCKITEM_24(wx,gtk) STOCKITEM(wx,gtk)
1531 #else
1532 #define STOCKITEM_24(wx,gtk) STOCKITEM_MISSING(wx)
1533 #endif
1534
1535 #if GTK_CHECK_VERSION(2,6,0)
1536 #define STOCKITEM_26(wx,gtk) STOCKITEM(wx,gtk)
1537 #else
1538 #define STOCKITEM_26(wx,gtk) STOCKITEM_MISSING(wx)
1539 #endif
1540
1541 #if GTK_CHECK_VERSION(2,10,0)
1542 #define STOCKITEM_210(wx,gtk) STOCKITEM(wx,gtk)
1543 #else
1544 #define STOCKITEM_210(wx,gtk) STOCKITEM_MISSING(wx)
1545 #endif
1546
1547
1548 switch (id)
1549 {
1550 STOCKITEM_26(wxID_ABOUT, GTK_STOCK_ABOUT)
1551 STOCKITEM(wxID_ADD, GTK_STOCK_ADD)
1552 STOCKITEM(wxID_APPLY, GTK_STOCK_APPLY)
1553 STOCKITEM(wxID_BOLD, GTK_STOCK_BOLD)
1554 STOCKITEM(wxID_CANCEL, GTK_STOCK_CANCEL)
1555 STOCKITEM(wxID_CLEAR, GTK_STOCK_CLEAR)
1556 STOCKITEM(wxID_CLOSE, GTK_STOCK_CLOSE)
1557 STOCKITEM(wxID_COPY, GTK_STOCK_COPY)
1558 STOCKITEM(wxID_CUT, GTK_STOCK_CUT)
1559 STOCKITEM(wxID_DELETE, GTK_STOCK_DELETE)
1560 STOCKITEM_26(wxID_EDIT, GTK_STOCK_EDIT)
1561 STOCKITEM(wxID_FIND, GTK_STOCK_FIND)
1562 STOCKITEM_26(wxID_FILE, GTK_STOCK_FILE)
1563 STOCKITEM(wxID_REPLACE, GTK_STOCK_FIND_AND_REPLACE)
1564 STOCKITEM(wxID_BACKWARD, GTK_STOCK_GO_BACK)
1565 STOCKITEM(wxID_DOWN, GTK_STOCK_GO_DOWN)
1566 STOCKITEM(wxID_FORWARD, GTK_STOCK_GO_FORWARD)
1567 STOCKITEM(wxID_UP, GTK_STOCK_GO_UP)
1568 STOCKITEM(wxID_HELP, GTK_STOCK_HELP)
1569 STOCKITEM(wxID_HOME, GTK_STOCK_HOME)
1570 STOCKITEM_24(wxID_INDENT, GTK_STOCK_INDENT)
1571 STOCKITEM(wxID_INDEX, GTK_STOCK_INDEX)
1572 STOCKITEM(wxID_ITALIC, GTK_STOCK_ITALIC)
1573 STOCKITEM(wxID_JUSTIFY_CENTER, GTK_STOCK_JUSTIFY_CENTER)
1574 STOCKITEM(wxID_JUSTIFY_FILL, GTK_STOCK_JUSTIFY_FILL)
1575 STOCKITEM(wxID_JUSTIFY_LEFT, GTK_STOCK_JUSTIFY_LEFT)
1576 STOCKITEM(wxID_JUSTIFY_RIGHT, GTK_STOCK_JUSTIFY_RIGHT)
1577 STOCKITEM(wxID_NEW, GTK_STOCK_NEW)
1578 STOCKITEM(wxID_NO, GTK_STOCK_NO)
1579 STOCKITEM(wxID_OK, GTK_STOCK_OK)
1580 STOCKITEM(wxID_OPEN, GTK_STOCK_OPEN)
1581 STOCKITEM(wxID_PASTE, GTK_STOCK_PASTE)
1582 STOCKITEM(wxID_PREFERENCES, GTK_STOCK_PREFERENCES)
1583 STOCKITEM(wxID_PRINT, GTK_STOCK_PRINT)
1584 STOCKITEM(wxID_PREVIEW, GTK_STOCK_PRINT_PREVIEW)
1585 STOCKITEM(wxID_PROPERTIES, GTK_STOCK_PROPERTIES)
1586 STOCKITEM(wxID_EXIT, GTK_STOCK_QUIT)
1587 STOCKITEM(wxID_REDO, GTK_STOCK_REDO)
1588 STOCKITEM(wxID_REFRESH, GTK_STOCK_REFRESH)
1589 STOCKITEM(wxID_REMOVE, GTK_STOCK_REMOVE)
1590 STOCKITEM(wxID_REVERT_TO_SAVED, GTK_STOCK_REVERT_TO_SAVED)
1591 STOCKITEM(wxID_SAVE, GTK_STOCK_SAVE)
1592 STOCKITEM(wxID_SAVEAS, GTK_STOCK_SAVE_AS)
1593 STOCKITEM_210(wxID_SELECTALL, GTK_STOCK_SELECT_ALL)
1594 STOCKITEM(wxID_STOP, GTK_STOCK_STOP)
1595 STOCKITEM(wxID_UNDELETE, GTK_STOCK_UNDELETE)
1596 STOCKITEM(wxID_UNDERLINE, GTK_STOCK_UNDERLINE)
1597 STOCKITEM(wxID_UNDO, GTK_STOCK_UNDO)
1598 STOCKITEM_24(wxID_UNINDENT, GTK_STOCK_UNINDENT)
1599 STOCKITEM(wxID_YES, GTK_STOCK_YES)
1600 STOCKITEM(wxID_ZOOM_100, GTK_STOCK_ZOOM_100)
1601 STOCKITEM(wxID_ZOOM_FIT, GTK_STOCK_ZOOM_FIT)
1602 STOCKITEM(wxID_ZOOM_IN, GTK_STOCK_ZOOM_IN)
1603 STOCKITEM(wxID_ZOOM_OUT, GTK_STOCK_ZOOM_OUT)
1604
1605 default:
1606 wxFAIL_MSG( _T("invalid stock item ID") );
1607 break;
1608 };
1609
1610 #undef STOCKITEM
1611
1612 return NULL;
1613}
1614
19abd352
PC
1615#if wxUSE_ACCEL
1616static
bcf881ef
JS
1617bool wxGetStockGtkAccelerator(const char *id, GdkModifierType *mod, guint *key)
1618{
1619 if (!id)
1620 return false;
1621
1622 GtkStockItem stock_item;
1623 if (gtk_stock_lookup (id, &stock_item))
1624 {
1625 if (key) *key = stock_item.keyval;
1626 if (mod) *mod = stock_item.modifier;
1627
1628 // some GTK stock items have zero values for the keyval;
1629 // it means that they do not have an accelerator...
1630 if (stock_item.keyval)
1631 return true;
1632 }
1633
1634 return false;
1635}
19abd352 1636#endif // wxUSE_ACCEL
bcf881ef 1637
28fcfbfe 1638#endif // wxUSE_MENUS