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