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