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