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