]> git.saurik.com Git - wxWidgets.git/blob - src/gtk/menu.cpp
underscors are handled better in the menu item labels
[wxWidgets.git] / src / gtk / menu.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: menu.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // Id: $Id$
6 // Copyright: (c) 1998 Robert Roebling
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 #ifdef __GNUG__
11 #pragma implementation "menu.h"
12 #pragma implementation "menuitem.h"
13 #endif
14
15 #include "wx/menu.h"
16 #include "wx/log.h"
17 #include "wx/intl.h"
18 #include "wx/app.h"
19
20 #include "gdk/gdk.h"
21 #include "gtk/gtk.h"
22
23 //-----------------------------------------------------------------------------
24 // idle system
25 //-----------------------------------------------------------------------------
26
27 extern void wxapp_install_idle_handler();
28 extern bool g_isIdle;
29
30 //-----------------------------------------------------------------------------
31 // wxMenuBar
32 //-----------------------------------------------------------------------------
33
34 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar,wxWindow)
35
36 wxMenuBar::wxMenuBar( long style )
37 {
38 /* the parent window is known after wxFrame::SetMenu() */
39 m_needParent = FALSE;
40 m_style = style;
41 m_invokingWindow = (wxWindow*) NULL;
42
43 if (!PreCreation( (wxWindow*) NULL, wxDefaultPosition, wxDefaultSize ) ||
44 !CreateBase( (wxWindow*) NULL, -1, wxDefaultPosition, wxDefaultSize, style, wxDefaultValidator, _T("menubar") ))
45 {
46 wxFAIL_MSG( _T("wxMenuBar creation failed") );
47 return;
48 }
49
50 m_menus.DeleteContents( TRUE );
51
52 /* GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has. */
53 #if (GTK_MINOR_VERSION > 0) && (GTK_MICRO_VERSION > 0)
54 m_accel = gtk_accel_group_new();
55 m_factory = gtk_item_factory_new( GTK_TYPE_MENU_BAR, "<main>", m_accel );
56 m_menubar = gtk_item_factory_get_widget( m_factory, "<main>" );
57 #else
58 m_menubar = gtk_menu_bar_new();
59 #endif
60
61 if (style & wxMB_DOCKABLE)
62 {
63 m_widget = gtk_handle_box_new();
64 gtk_container_add( GTK_CONTAINER(m_widget), GTK_WIDGET(m_menubar) );
65 gtk_widget_show( GTK_WIDGET(m_menubar) );
66 }
67 else
68 {
69 m_widget = GTK_WIDGET(m_menubar);
70 }
71
72 PostCreation();
73 }
74
75 wxMenuBar::wxMenuBar()
76 {
77 /* the parent window is known after wxFrame::SetMenu() */
78 m_needParent = FALSE;
79 m_style = 0;
80 m_invokingWindow = (wxWindow*) NULL;
81
82 if (!PreCreation( (wxWindow*) NULL, wxDefaultPosition, wxDefaultSize ) ||
83 !CreateBase( (wxWindow*) NULL, -1, wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("menubar") ))
84 {
85 wxFAIL_MSG( _T("wxMenuBar creation failed") );
86 return;
87 }
88
89 m_menus.DeleteContents( TRUE );
90
91 /* GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has. */
92 #if (GTK_MINOR_VERSION > 0) && (GTK_MICRO_VERSION > 0)
93 m_accel = gtk_accel_group_new();
94 m_factory = gtk_item_factory_new( GTK_TYPE_MENU_BAR, "<main>", m_accel );
95 m_menubar = gtk_item_factory_get_widget( m_factory, "<main>" );
96 #else
97 m_menubar = gtk_menu_bar_new();
98 #endif
99
100 m_widget = GTK_WIDGET(m_menubar);
101
102 PostCreation();
103 }
104
105 wxMenuBar::~wxMenuBar()
106 {
107 // how to destroy a GtkItemFactory ?
108 }
109
110 static void wxMenubarUnsetInvokingWindow( wxMenu *menu, wxWindow *win )
111 {
112 menu->SetInvokingWindow( (wxWindow*) NULL );
113
114 #if (GTK_MINOR_VERSION > 0)
115 wxWindow *top_frame = win;
116 while (top_frame->GetParent()) top_frame = top_frame->GetParent();
117
118 /* support for native hot keys */
119 gtk_accel_group_detach( menu->m_accel, GTK_OBJECT(top_frame->m_widget) );
120 #endif
121
122 wxNode *node = menu->GetItems().First();
123 while (node)
124 {
125 wxMenuItem *menuitem = (wxMenuItem*)node->Data();
126 if (menuitem->IsSubMenu())
127 wxMenubarUnsetInvokingWindow( menuitem->GetSubMenu(), win );
128 node = node->Next();
129 }
130 }
131
132 static void wxMenubarSetInvokingWindow( wxMenu *menu, wxWindow *win )
133 {
134 menu->SetInvokingWindow( win );
135
136 #if (GTK_MINOR_VERSION > 0)
137 wxWindow *top_frame = win;
138 while (top_frame->GetParent())
139 top_frame = top_frame->GetParent();
140
141 /* support for native hot keys */
142 gtk_accel_group_attach( menu->m_accel, GTK_OBJECT(top_frame->m_widget) );
143 #endif
144
145 wxNode *node = menu->GetItems().First();
146 while (node)
147 {
148 wxMenuItem *menuitem = (wxMenuItem*)node->Data();
149 if (menuitem->IsSubMenu())
150 wxMenubarSetInvokingWindow( menuitem->GetSubMenu(), win );
151 node = node->Next();
152 }
153 }
154
155 void wxMenuBar::SetInvokingWindow( wxWindow *win )
156 {
157 m_invokingWindow = win;
158 #if (GTK_MINOR_VERSION > 0) && (GTK_MICRO_VERSION > 0)
159 wxWindow *top_frame = win;
160 while (top_frame->GetParent())
161 top_frame = top_frame->GetParent();
162
163 /* support for native key accelerators indicated by underscroes */
164 gtk_accel_group_attach( m_accel, GTK_OBJECT(top_frame->m_widget) );
165 #endif
166
167 wxNode *node = m_menus.First();
168 while (node)
169 {
170 wxMenu *menu = (wxMenu*)node->Data();
171 wxMenubarSetInvokingWindow( menu, win );
172 node = node->Next();
173 }
174 }
175
176 void wxMenuBar::UnsetInvokingWindow( wxWindow *win )
177 {
178 m_invokingWindow = (wxWindow*) NULL;
179 #if (GTK_MINOR_VERSION > 0) && (GTK_MICRO_VERSION > 0)
180 wxWindow *top_frame = win;
181 while (top_frame->GetParent())
182 top_frame = top_frame->GetParent();
183
184 /* support for native key accelerators indicated by underscroes */
185 gtk_accel_group_detach( m_accel, GTK_OBJECT(top_frame->m_widget) );
186 #endif
187
188 wxNode *node = m_menus.First();
189 while (node)
190 {
191 wxMenu *menu = (wxMenu*)node->Data();
192 wxMenubarUnsetInvokingWindow( menu, win );
193 node = node->Next();
194 }
195 }
196
197 void wxMenuBar::Append( wxMenu *menu, const wxString &title )
198 {
199 m_menus.Append( menu );
200
201 const wxChar *pc;
202
203 /* GTK 1.2 wants to have "_" instead of "&" for accelerators */
204 wxString str;
205 for ( pc = title; *pc != _T('\0'); pc++ )
206 {
207 if (*pc == _T('&'))
208 {
209 #if (GTK_MINOR_VERSION > 0) && (GTK_MICRO_VERSION > 0)
210 str << _T('_');
211 }
212 else if (*pc == _T('/'))
213 {
214 str << _T('\\');
215 #endif
216 }
217 else
218 {
219 if ( *pc == _T('_') )
220 {
221 // underscores must be doubled to prevent them from being
222 // interpreted as accelerator character prefix by GTK
223 str << *pc;
224 }
225
226 str << *pc;
227 }
228 }
229
230 /* this doesn't have much effect right now */
231 menu->SetTitle( str );
232
233 /* GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has. */
234 #if (GTK_MINOR_VERSION > 0) && (GTK_MICRO_VERSION > 0)
235
236 /* local buffer in multibyte form */
237 wxString buf;
238 buf << _T('/') << str.c_str();
239
240 char *cbuf = new char[buf.Length()+1];
241 strcpy(cbuf, buf.mbc_str());
242
243 GtkItemFactoryEntry entry;
244 entry.path = (gchar *)cbuf; // const_cast
245 entry.accelerator = (gchar*) NULL;
246 entry.callback = (GtkItemFactoryCallback) NULL;
247 entry.callback_action = 0;
248 entry.item_type = "<Branch>";
249
250 gtk_item_factory_create_item( m_factory, &entry, (gpointer) this, 2 ); /* what is 2 ? */
251 /* in order to get the pointer to the item we need the item text _without_ underscores */
252 wxString tmp = _T("<main>/");
253 for ( pc = str; *pc != _T('\0'); pc++ )
254 {
255 // contrary to the common sense, we must throw out _all_ underscores,
256 // (i.e. "Hello__World" => "HelloWorld" and not "Hello_World" as we
257 // might naively think). IMHO it's a bug in GTK+ (VZ)
258 while (*pc == _T('_'))
259 pc++;
260 tmp << *pc;
261 }
262 menu->m_owner = gtk_item_factory_get_item( m_factory, tmp.mb_str() );
263 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu->m_owner), menu->m_menu );
264 delete [] cbuf;
265 #else
266
267 menu->m_owner = gtk_menu_item_new_with_label( str.mb_str() );
268 gtk_widget_show( menu->m_owner );
269 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu->m_owner), menu->m_menu );
270
271 gtk_menu_bar_append( GTK_MENU_BAR(m_menubar), menu->m_owner );
272
273 #endif
274
275 // m_invokingWindow is set after wxFrame::SetMenuBar(). This call enables
276 // adding menu later on.
277 if (m_invokingWindow)
278 wxMenubarSetInvokingWindow( menu, m_invokingWindow );
279 }
280
281 static int FindMenuItemRecursive( const wxMenu *menu, const wxString &menuString, const wxString &itemString )
282 {
283 if (menu->GetTitle() == menuString)
284 {
285 int res = menu->FindItem( itemString );
286 if (res != wxNOT_FOUND)
287 return res;
288 }
289
290 wxNode *node = ((wxMenu *)menu)->GetItems().First(); // const_cast
291 while (node)
292 {
293 wxMenuItem *item = (wxMenuItem*)node->Data();
294 if (item->IsSubMenu())
295 return FindMenuItemRecursive(item->GetSubMenu(), menuString, itemString);
296
297 node = node->Next();
298 }
299
300 return wxNOT_FOUND;
301 }
302
303 wxMenuItem *wxMenuBar::FindItemForId(int itemId, wxMenu **menuForItem ) const
304 {
305 if ( menuForItem )
306 {
307 // TODO return the pointer to the menu
308
309 *menuForItem = NULL;
310 }
311
312 return FindItem(itemId);
313 }
314
315 int wxMenuBar::FindMenuItem( const wxString &menuString, const wxString &itemString ) const
316 {
317 wxNode *node = m_menus.First();
318 while (node)
319 {
320 wxMenu *menu = (wxMenu*)node->Data();
321 int res = FindMenuItemRecursive( menu, menuString, itemString);
322 if (res != -1) return res;
323 node = node->Next();
324 }
325 return -1;
326 }
327
328 // Find a wxMenuItem using its id. Recurses down into sub-menus
329 static wxMenuItem* FindMenuItemByIdRecursive(const wxMenu* menu, int id)
330 {
331 wxMenuItem* result = menu->FindItem(id);
332
333 wxNode *node = ((wxMenu *)menu)->GetItems().First(); // const_cast
334 while ( node && result == NULL )
335 {
336 wxMenuItem *item = (wxMenuItem*)node->Data();
337 if (item->IsSubMenu())
338 {
339 result = FindMenuItemByIdRecursive( item->GetSubMenu(), id );
340 }
341 node = node->Next();
342 }
343
344 return result;
345 }
346
347 wxMenuItem* wxMenuBar::FindItem( int id ) const
348 {
349 wxMenuItem* result = 0;
350 wxNode *node = m_menus.First();
351 while (node && result == 0)
352 {
353 wxMenu *menu = (wxMenu*)node->Data();
354 result = FindMenuItemByIdRecursive( menu, id );
355 node = node->Next();
356 }
357
358 return result;
359 }
360
361 void wxMenuBar::Check( int id, bool check )
362 {
363 wxMenuItem* item = FindMenuItemById( id );
364
365 wxCHECK_RET( item, _T("wxMenuBar::Check: no such item") );
366
367 item->Check(check);
368 }
369
370 bool wxMenuBar::IsChecked( int id ) const
371 {
372 wxMenuItem* item = FindMenuItemById( id );
373
374 wxCHECK_MSG( item, FALSE, _T("wxMenuBar::IsChecked: no such item") );
375
376 return item->IsChecked();
377 }
378
379 void wxMenuBar::Enable( int id, bool enable )
380 {
381 wxMenuItem* item = FindMenuItemById( id );
382
383 wxCHECK_RET( item, _T("wxMenuBar::Enable: no such item") );
384
385 item->Enable(enable);
386 }
387
388 bool wxMenuBar::IsEnabled( int id ) const
389 {
390 wxMenuItem* item = FindMenuItemById( id );
391
392 wxCHECK_MSG( item, FALSE, _T("wxMenuBar::IsEnabled: no such item") );
393
394 return item->IsEnabled();
395 }
396
397 wxString wxMenuBar::GetLabel( int id ) const
398 {
399 wxMenuItem* item = FindMenuItemById( id );
400
401 wxCHECK_MSG( item, _T(""), _T("wxMenuBar::GetLabel: no such item") );
402
403 return item->GetText();
404 }
405
406 void wxMenuBar::SetLabel( int id, const wxString &label )
407 {
408 wxMenuItem* item = FindMenuItemById( id );
409
410 wxCHECK_RET( item, _T("wxMenuBar::SetLabel: no such item") );
411
412 item->SetText( label );
413 }
414
415 void wxMenuBar::EnableTop( int pos, bool flag )
416 {
417 wxNode *node = m_menus.Nth( pos );
418
419 wxCHECK_RET( node, _T("menu not found") );
420
421 wxMenu* menu = (wxMenu*)node->Data();
422
423 if (menu->m_owner)
424 gtk_widget_set_sensitive( menu->m_owner, flag );
425 }
426
427 wxString wxMenuBar::GetLabelTop( int pos ) const
428 {
429 wxNode *node = m_menus.Nth( pos );
430
431 wxCHECK_MSG( node, _T("invalid"), _T("menu not found") );
432
433 wxMenu* menu = (wxMenu*)node->Data();
434
435 return menu->GetTitle();
436 }
437
438 void wxMenuBar::SetLabelTop( int pos, const wxString& label )
439 {
440 wxNode *node = m_menus.Nth( pos );
441
442 wxCHECK_RET( node, _T("menu not found") );
443
444 wxMenu* menu = (wxMenu*)node->Data();
445
446 menu->SetTitle( label );
447 }
448
449 void wxMenuBar::SetHelpString( int id, const wxString& helpString )
450 {
451 wxMenuItem* item = FindMenuItemById( id );
452
453 wxCHECK_RET( item, _T("wxMenuBar::SetHelpString: no such item") );
454
455 item->SetHelp( helpString );
456 }
457
458 wxString wxMenuBar::GetHelpString( int id ) const
459 {
460 wxMenuItem* item = FindMenuItemById( id );
461
462 wxCHECK_MSG( item, _T(""), _T("wxMenuBar::GetHelpString: no such item") );
463
464 return item->GetHelp();
465 }
466
467 //-----------------------------------------------------------------------------
468 // "activate"
469 //-----------------------------------------------------------------------------
470
471 static void gtk_menu_clicked_callback( GtkWidget *widget, wxMenu *menu )
472 {
473 if (g_isIdle) wxapp_install_idle_handler();
474
475 int id = menu->FindMenuIdByMenuItem(widget);
476
477 /* should find it for normal (not popup) menu */
478 wxASSERT( (id != -1) || (menu->GetInvokingWindow() != NULL) );
479
480 if (!menu->IsEnabled(id))
481 return;
482
483 wxMenuItem* item = menu->FindItem( id );
484 wxCHECK_RET( item, _T("error in menu item callback") );
485
486 if (item->IsCheckable())
487 {
488 if (item->GetCheckedFlag() == item->IsChecked())
489 {
490 /* the menu item has been checked by calling wxMenuItem->Check() */
491 return;
492 }
493 else
494 {
495 /* the user pressed on the menu item -> report */
496 item->SetCheckedFlag(item->IsChecked()); /* make consistent again */
497 }
498 }
499
500 wxCommandEvent event( wxEVT_COMMAND_MENU_SELECTED, id );
501 event.SetEventObject( menu );
502 event.SetInt(id );
503
504 if (menu->GetCallback())
505 {
506 (void) (*(menu->GetCallback())) (*menu, event);
507 return;
508 }
509
510 if (menu->GetEventHandler()->ProcessEvent(event))
511 return;
512
513 wxWindow *win = menu->GetInvokingWindow();
514 if (win)
515 win->GetEventHandler()->ProcessEvent( event );
516 }
517
518 //-----------------------------------------------------------------------------
519 // "select"
520 //-----------------------------------------------------------------------------
521
522 static void gtk_menu_hilight_callback( GtkWidget *widget, wxMenu *menu )
523 {
524 if (g_isIdle) wxapp_install_idle_handler();
525
526 int id = menu->FindMenuIdByMenuItem(widget);
527
528 wxASSERT( id != -1 ); // should find it!
529
530 if (!menu->IsEnabled(id))
531 return;
532
533 wxMenuEvent event( wxEVT_MENU_HIGHLIGHT, id );
534 event.SetEventObject( menu );
535
536 if (menu->GetEventHandler()->ProcessEvent(event))
537 return;
538
539 wxWindow *win = menu->GetInvokingWindow();
540 if (win) win->GetEventHandler()->ProcessEvent( event );
541 }
542
543 //-----------------------------------------------------------------------------
544 // "deselect"
545 //-----------------------------------------------------------------------------
546
547 static void gtk_menu_nolight_callback( GtkWidget *widget, wxMenu *menu )
548 {
549 if (g_isIdle) wxapp_install_idle_handler();
550
551 int id = menu->FindMenuIdByMenuItem(widget);
552
553 wxASSERT( id != -1 ); // should find it!
554
555 if (!menu->IsEnabled(id))
556 return;
557
558 wxMenuEvent event( wxEVT_MENU_HIGHLIGHT, -1 );
559 event.SetEventObject( menu );
560
561 if (menu->GetEventHandler()->ProcessEvent(event))
562 return;
563
564 wxWindow *win = menu->GetInvokingWindow();
565 if (win)
566 win->GetEventHandler()->ProcessEvent( event );
567 }
568
569 //-----------------------------------------------------------------------------
570 // wxMenuItem
571 //-----------------------------------------------------------------------------
572
573 IMPLEMENT_DYNAMIC_CLASS(wxMenuItem,wxObject)
574
575 wxMenuItem::wxMenuItem()
576 {
577 m_id = ID_SEPARATOR;
578 m_isCheckMenu = FALSE;
579 m_isChecked = FALSE;
580 m_isEnabled = TRUE;
581 m_subMenu = (wxMenu *) NULL;
582 m_menuItem = (GtkWidget *) NULL;
583 }
584
585 // it's valid for this function to be called even if m_menuItem == NULL
586 void wxMenuItem::SetName( const wxString& str )
587 {
588 /* '\t' is the deliminator indicating a hot key */
589 m_text = _T("");
590 const wxChar *pc = str;
591 for (; (*pc != _T('\0')) && (*pc != _T('\t')); pc++ )
592 {
593 if (*pc == _T('&'))
594 {
595 #if (GTK_MINOR_VERSION > 0)
596 m_text << _T('_');
597 } else
598 if (*pc == _T('/')) /* we have to filter out slashes ... */
599 {
600 m_text << _T('\\'); /* ... and replace them with back slashes */
601 #endif
602 }
603 else
604 m_text << *pc;
605 }
606
607 /* only GTK 1.2 knows about hot keys */
608 m_hotKey = _T("");
609 #if (GTK_MINOR_VERSION > 0)
610 if(*pc == _T('\t'))
611 {
612 pc++;
613 m_hotKey = pc;
614 }
615 #endif
616
617 if (m_menuItem)
618 {
619 GtkLabel *label = GTK_LABEL( GTK_BIN(m_menuItem)->child );
620 gtk_label_set( label, m_text.mb_str());
621 }
622 }
623
624 void wxMenuItem::Check( bool check )
625 {
626 wxCHECK_RET( m_menuItem, _T("invalid menu item") );
627
628 wxCHECK_RET( IsCheckable(), _T("Can't check uncheckable item!") )
629
630 if (check == m_isChecked) return;
631
632 m_isChecked = check;
633 gtk_check_menu_item_set_state( (GtkCheckMenuItem*)m_menuItem, (gint)check );
634 }
635
636 void wxMenuItem::Enable( bool enable )
637 {
638 wxCHECK_RET( m_menuItem, _T("invalid menu item") );
639
640 gtk_widget_set_sensitive( m_menuItem, enable );
641 m_isEnabled = enable;
642 }
643
644 bool wxMenuItem::IsChecked() const
645 {
646 wxCHECK_MSG( m_menuItem, FALSE, _T("invalid menu item") );
647
648 wxCHECK( IsCheckable(), FALSE ); // can't get state of uncheckable item!
649
650 bool bIsChecked = ((GtkCheckMenuItem*)m_menuItem)->active != 0;
651
652 return bIsChecked;
653 }
654
655 //-----------------------------------------------------------------------------
656 // wxMenu
657 //-----------------------------------------------------------------------------
658
659 IMPLEMENT_DYNAMIC_CLASS(wxMenu,wxEvtHandler)
660
661 void
662 wxMenu::Init( const wxString& title,
663 long style,
664 const wxFunction func
665 )
666 {
667 m_title = title;
668 m_items.DeleteContents( TRUE );
669 m_invokingWindow = (wxWindow *) NULL;
670 m_style = style;
671
672 #if (GTK_MINOR_VERSION > 0)
673 m_accel = gtk_accel_group_new();
674 m_factory = gtk_item_factory_new( GTK_TYPE_MENU, "<main>", m_accel );
675 m_menu = gtk_item_factory_get_widget( m_factory, "<main>" );
676 #else
677 m_menu = gtk_menu_new(); // Do not show!
678 #endif
679
680 m_callback = func;
681
682 m_eventHandler = this;
683 m_clientData = (void*) NULL;
684
685 if (m_title.IsNull()) m_title = _T("");
686 if (m_title != _T(""))
687 {
688 Append(-2, m_title);
689 AppendSeparator();
690 }
691
692 m_owner = (GtkWidget*) NULL;
693
694 #if (GTK_MINOR_VERSION > 0)
695 /* Tearoffs are entries, just like separators. So if we want this
696 menu to be a tear-off one, we just append a tearoff entry
697 immediately. */
698 if(m_style & wxMENU_TEAROFF)
699 {
700 GtkItemFactoryEntry entry;
701 entry.path = "/tearoff";
702 entry.callback = (GtkItemFactoryCallback) NULL;
703 entry.callback_action = 0;
704 entry.item_type = "<Tearoff>";
705 entry.accelerator = (gchar*) NULL;
706 gtk_item_factory_create_item( m_factory, &entry, (gpointer) this, 2 ); /* what is 2 ? */
707 //GtkWidget *menuItem = gtk_item_factory_get_widget( m_factory, "<main>/tearoff" );
708 }
709 #endif
710 }
711
712 wxMenu::~wxMenu()
713 {
714 /* how do we delete an item-factory ? */
715 gtk_widget_destroy( m_menu );
716
717 }
718
719 void wxMenu::SetTitle( const wxString& title )
720 {
721 // TODO Waiting for something better
722 m_title = title;
723 }
724
725 const wxString wxMenu::GetTitle() const
726 {
727 return m_title;
728 }
729
730 void wxMenu::AppendSeparator()
731 {
732 wxMenuItem *mitem = new wxMenuItem();
733 mitem->SetId(ID_SEPARATOR);
734
735 #if (GTK_MINOR_VERSION > 0)
736 GtkItemFactoryEntry entry;
737 entry.path = "/sep";
738 entry.callback = (GtkItemFactoryCallback) NULL;
739 entry.callback_action = 0;
740 entry.item_type = "<Separator>";
741 entry.accelerator = (gchar*) NULL;
742
743 gtk_item_factory_create_item( m_factory, &entry, (gpointer) this, 2 ); /* what is 2 ? */
744
745 /* this will be wrong for more than one separator. do we care? */
746 GtkWidget *menuItem = gtk_item_factory_get_widget( m_factory, "<main>/sep" );
747 #else
748 GtkWidget *menuItem = gtk_menu_item_new();
749 gtk_menu_append( GTK_MENU(m_menu), menuItem );
750 gtk_widget_show( menuItem );
751 #endif
752
753 mitem->SetMenuItem(menuItem);
754 m_items.Append( mitem );
755 }
756
757 #if (GTK_MINOR_VERSION > 0)
758 static char* GetHotKey( const wxString &hotkey, char *hotbuf )
759 {
760 if (hotkey.IsEmpty()) return (char*) NULL;
761
762 switch (hotkey[0])
763 {
764 case _T('a'): /* Alt */
765 case _T('A'):
766 case _T('m'): /* Meta */
767 case _T('M'):
768 {
769 strcpy( hotbuf, "<alt>" );
770 wxString last = hotkey.Right(1);
771 strcat( hotbuf, last.mb_str() );
772 return hotbuf;
773 }
774 case _T('c'): /* Ctrl */
775 case _T('C'):
776 case _T('s'): /* Strg, yeah man, I'm German */
777 case _T('S'):
778 {
779 strcpy( hotbuf, "<control>" );
780 wxString last = hotkey.Right(1);
781 strcat( hotbuf, last.mb_str() );
782 return hotbuf;
783 }
784 case _T('F'): /* function keys */
785 {
786 strcpy( hotbuf, hotkey.mb_str() );
787 return hotbuf;
788 }
789 default:
790 {
791 }
792 }
793 return (char*) NULL;
794 }
795 #endif
796
797 void wxMenu::Append( int id, const wxString &item, const wxString &helpStr, bool checkable )
798 {
799 wxMenuItem *mitem = new wxMenuItem();
800 mitem->SetId(id);
801 mitem->SetText(item);
802 mitem->SetHelp(helpStr);
803 mitem->SetCheckable(checkable);
804
805 #if (GTK_MINOR_VERSION > 0)
806 /* text has "_" instead of "&" after mitem->SetText() */
807 wxString text( mitem->GetText() );
808
809 /* local buffer in multibyte form */
810 char buf[200];
811 strcpy( buf, "/" );
812 strcat( buf, text.mb_str() );
813
814 GtkItemFactoryEntry entry;
815 entry.path = buf;
816 entry.callback = (GtkItemFactoryCallback) gtk_menu_clicked_callback;
817 entry.callback_action = 0;
818 if (checkable)
819 entry.item_type = "<CheckItem>";
820 else
821 entry.item_type = "<Item>";
822
823 char hotbuf[50];
824 entry.accelerator = GetHotKey( mitem->GetHotKey(), hotbuf );
825
826 gtk_item_factory_create_item( m_factory, &entry, (gpointer) this, 2 ); /* what is 2 ? */
827
828 /* in order to get the pointer to the item we need the item text _without_ underscores */
829 wxString s = _T("<main>/");
830 for ( const wxChar *pc = text; *pc != _T('\0'); pc++ )
831 {
832 if (*pc == _T('_')) pc++; /* skip it */
833 s << *pc;
834 }
835
836 GtkWidget *menuItem = gtk_item_factory_get_widget( m_factory, s.mb_str() );
837
838 #else
839
840 GtkWidget *menuItem = checkable ? gtk_check_menu_item_new_with_label( mitem->GetText().mb_str() )
841 : gtk_menu_item_new_with_label( mitem->GetText().mb_str() );
842
843 gtk_signal_connect( GTK_OBJECT(menuItem), "activate",
844 GTK_SIGNAL_FUNC(gtk_menu_clicked_callback),
845 (gpointer)this );
846
847 gtk_menu_append( GTK_MENU(m_menu), menuItem );
848 gtk_widget_show( menuItem );
849
850 #endif
851
852 gtk_signal_connect( GTK_OBJECT(menuItem), "select",
853 GTK_SIGNAL_FUNC(gtk_menu_hilight_callback),
854 (gpointer)this );
855
856 gtk_signal_connect( GTK_OBJECT(menuItem), "deselect",
857 GTK_SIGNAL_FUNC(gtk_menu_nolight_callback),
858 (gpointer)this );
859
860 mitem->SetMenuItem(menuItem);
861
862 m_items.Append( mitem );
863 }
864
865 void wxMenu::Append( int id, const wxString &item, wxMenu *subMenu, const wxString &helpStr )
866 {
867 wxMenuItem *mitem = new wxMenuItem();
868 mitem->SetId(id);
869 mitem->SetText(item);
870 mitem->SetHelp(helpStr);
871
872 #if (GTK_MINOR_VERSION > 0)
873 /* text has "_" instead of "&" after mitem->SetText() */
874 wxString text( mitem->GetText() );
875
876 /* local buffer in multibyte form */
877 char buf[200];
878 strcpy( buf, "/" );
879 strcat( buf, text.mb_str() );
880
881 GtkItemFactoryEntry entry;
882 entry.path = buf;
883 entry.callback = (GtkItemFactoryCallback) 0;
884 entry.callback_action = 0;
885 entry.item_type = "<Branch>";
886
887 gtk_item_factory_create_item( m_factory, &entry, (gpointer) this, 2 ); /* what is 2 ? */
888
889 /* in order to get the pointer to the item we need the item text _without_ underscores */
890 wxString s = _T("<main>/");
891 for ( const wxChar *pc = text; *pc != _T('\0'); pc++ )
892 {
893 if (*pc == _T('_')) pc++; /* skip it */
894 s << *pc;
895 }
896
897 GtkWidget *menuItem = gtk_item_factory_get_item( m_factory, s.mb_str() );
898
899 #else
900
901 GtkWidget *menuItem = gtk_menu_item_new_with_label(mitem->GetText().mbc_str());
902
903 gtk_menu_append( GTK_MENU(m_menu), menuItem );
904 gtk_widget_show( menuItem );
905
906 #endif
907
908 gtk_signal_connect( GTK_OBJECT(menuItem), "select",
909 GTK_SIGNAL_FUNC(gtk_menu_hilight_callback),
910 (gpointer*)this );
911
912 gtk_signal_connect( GTK_OBJECT(menuItem), "deselect",
913 GTK_SIGNAL_FUNC(gtk_menu_nolight_callback),
914 (gpointer*)this );
915
916 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menuItem), subMenu->m_menu );
917
918 mitem->SetMenuItem(menuItem);
919 mitem->SetSubMenu(subMenu);
920
921 m_items.Append( mitem );
922 }
923
924 void wxMenu::Append( wxMenuItem *item )
925 {
926 m_items.Append( item );
927
928 GtkWidget *menuItem = (GtkWidget*) NULL;
929
930 if (item->IsSeparator())
931 menuItem = gtk_menu_item_new();
932 else if (item->IsSubMenu())
933 menuItem = gtk_menu_item_new_with_label(item->GetText().mbc_str());
934 else
935 menuItem = item->IsCheckable() ? gtk_check_menu_item_new_with_label(item->GetText().mbc_str())
936 : gtk_menu_item_new_with_label(item->GetText().mbc_str());
937
938 if (!item->IsSeparator())
939 {
940 gtk_signal_connect( GTK_OBJECT(menuItem), "select",
941 GTK_SIGNAL_FUNC(gtk_menu_hilight_callback),
942 (gpointer*)this );
943
944 gtk_signal_connect( GTK_OBJECT(menuItem), "deselect",
945 GTK_SIGNAL_FUNC(gtk_menu_nolight_callback),
946 (gpointer*)this );
947
948 if (!item->IsSubMenu())
949 {
950 gtk_signal_connect( GTK_OBJECT(menuItem), "activate",
951 GTK_SIGNAL_FUNC(gtk_menu_clicked_callback),
952 (gpointer*)this );
953 }
954 }
955
956 gtk_menu_append( GTK_MENU(m_menu), menuItem );
957 gtk_widget_show( menuItem );
958 item->SetMenuItem(menuItem);
959 }
960
961 int wxMenu::FindItem( const wxString itemString ) const
962 {
963 wxString s = _T("");
964 for ( const wxChar *pc = itemString; *pc != _T('\0'); pc++ )
965 {
966 if (*pc == _T('&'))
967 {
968 pc++; /* skip it */
969 #if (GTK_MINOR_VERSION > 0)
970 s << _T('_');
971 #endif
972 }
973 s << *pc;
974 }
975
976 wxNode *node = m_items.First();
977 while (node)
978 {
979 wxMenuItem *item = (wxMenuItem*)node->Data();
980 if (item->GetText() == s)
981 {
982 return item->GetId();
983 }
984 node = node->Next();
985 }
986
987 return wxNOT_FOUND;
988 }
989
990 void wxMenu::Enable( int id, bool enable )
991 {
992 wxMenuItem *item = FindItem(id);
993
994 wxCHECK_RET( item, _T("wxMenu::Enable: no such item") );
995
996 item->Enable(enable);
997 }
998
999 bool wxMenu::IsEnabled( int id ) const
1000 {
1001 wxMenuItem *item = FindItem(id);
1002
1003 wxCHECK_MSG( item, FALSE, _T("wxMenu::IsEnabled: no such item") );
1004
1005 return item->IsEnabled();
1006 }
1007
1008 void wxMenu::Check( int id, bool enable )
1009 {
1010 wxMenuItem *item = FindItem(id);
1011
1012 wxCHECK_RET( item, _T("wxMenu::Check: no such item") );
1013
1014 item->Check(enable);
1015 }
1016
1017 bool wxMenu::IsChecked( int id ) const
1018 {
1019 wxMenuItem *item = FindItem(id);
1020
1021 wxCHECK_MSG( item, FALSE, _T("wxMenu::IsChecked: no such item") );
1022
1023 return item->IsChecked();
1024 }
1025
1026 void wxMenu::SetLabel( int id, const wxString &label )
1027 {
1028 wxMenuItem *item = FindItem(id);
1029
1030 wxCHECK_RET( item, _T("wxMenu::SetLabel: no such item") );
1031
1032 item->SetText(label);
1033 }
1034
1035 wxString wxMenu::GetLabel( int id ) const
1036 {
1037 wxMenuItem *item = FindItem(id);
1038
1039 wxCHECK_MSG( item, _T(""), _T("wxMenu::GetLabel: no such item") );
1040
1041 return item->GetText();
1042 }
1043
1044 void wxMenu::SetHelpString( int id, const wxString& helpString )
1045 {
1046 wxMenuItem *item = FindItem(id);
1047
1048 wxCHECK_RET( item, _T("wxMenu::SetHelpString: no such item") );
1049
1050 item->SetHelp( helpString );
1051 }
1052
1053 wxString wxMenu::GetHelpString( int id ) const
1054 {
1055 wxMenuItem *item = FindItem(id);
1056
1057 wxCHECK_MSG( item, _T(""), _T("wxMenu::GetHelpString: no such item") );
1058
1059 return item->GetHelp();
1060 }
1061
1062 int wxMenu::FindMenuIdByMenuItem( GtkWidget *menuItem ) const
1063 {
1064 wxNode *node = m_items.First();
1065 while (node)
1066 {
1067 wxMenuItem *item = (wxMenuItem*)node->Data();
1068 if (item->GetMenuItem() == menuItem)
1069 return item->GetId();
1070 node = node->Next();
1071 }
1072
1073 return wxNOT_FOUND;
1074 }
1075
1076 wxMenuItem *wxMenu::FindItem(int id) const
1077 {
1078 wxNode *node = m_items.First();
1079 while (node)
1080 {
1081 wxMenuItem *item = (wxMenuItem*)node->Data();
1082 if (item->GetId() == id)
1083 {
1084 return item;
1085 }
1086 node = node->Next();
1087 }
1088
1089 /* Not finding anything here can be correct
1090 * when search the entire menu system for
1091 * an entry -> no error message. */
1092
1093 return (wxMenuItem *) NULL;
1094 }
1095
1096 void wxMenu::SetInvokingWindow( wxWindow *win )
1097 {
1098 m_invokingWindow = win;
1099 }
1100
1101 wxWindow *wxMenu::GetInvokingWindow()
1102 {
1103 return m_invokingWindow;
1104 }
1105
1106 // Update a menu and all submenus recursively. source is the object that has
1107 // the update event handlers defined for it. If NULL, the menu or associated
1108 // window will be used.
1109 void wxMenu::UpdateUI(wxEvtHandler* source)
1110 {
1111 if (!source && GetInvokingWindow())
1112 source = GetInvokingWindow()->GetEventHandler();
1113 if (!source)
1114 source = GetEventHandler();
1115 if (!source)
1116 source = this;
1117
1118 wxNode* node = GetItems().First();
1119 while (node)
1120 {
1121 wxMenuItem* item = (wxMenuItem*) node->Data();
1122 if ( !item->IsSeparator() )
1123 {
1124 wxWindowID id = item->GetId();
1125 wxUpdateUIEvent event(id);
1126 event.SetEventObject( source );
1127
1128 if (source->ProcessEvent(event))
1129 {
1130 if (event.GetSetText())
1131 SetLabel(id, event.GetText());
1132 if (event.GetSetChecked())
1133 Check(id, event.GetChecked());
1134 if (event.GetSetEnabled())
1135 Enable(id, event.GetEnabled());
1136 }
1137
1138 if (item->GetSubMenu())
1139 item->GetSubMenu()->UpdateUI(source);
1140 }
1141 node = node->Next();
1142 }
1143 }
1144