]> git.saurik.com Git - wxWidgets.git/blob - src/gtk1/menu.cpp
underscores in menu items handled too under GTK+ 1.2
[wxWidgets.git] / src / gtk1 / 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 }
598 else if ( *pc == _T('_') ) // escape underscores
599 {
600 m_text << _T("__");
601 }
602 else if (*pc == _T('/')) /* we have to filter out slashes ... */
603 {
604 m_text << _T('\\'); /* ... and replace them with back slashes */
605 #endif
606 }
607 else
608 m_text << *pc;
609 }
610
611 /* only GTK 1.2 knows about hot keys */
612 m_hotKey = _T("");
613 #if (GTK_MINOR_VERSION > 0)
614 if(*pc == _T('\t'))
615 {
616 pc++;
617 m_hotKey = pc;
618 }
619 #endif
620
621 if (m_menuItem)
622 {
623 GtkLabel *label = GTK_LABEL( GTK_BIN(m_menuItem)->child );
624 gtk_label_set( label, m_text.mb_str());
625 }
626 }
627
628 void wxMenuItem::Check( bool check )
629 {
630 wxCHECK_RET( m_menuItem, _T("invalid menu item") );
631
632 wxCHECK_RET( IsCheckable(), _T("Can't check uncheckable item!") )
633
634 if (check == m_isChecked) return;
635
636 m_isChecked = check;
637 gtk_check_menu_item_set_state( (GtkCheckMenuItem*)m_menuItem, (gint)check );
638 }
639
640 void wxMenuItem::Enable( bool enable )
641 {
642 wxCHECK_RET( m_menuItem, _T("invalid menu item") );
643
644 gtk_widget_set_sensitive( m_menuItem, enable );
645 m_isEnabled = enable;
646 }
647
648 bool wxMenuItem::IsChecked() const
649 {
650 wxCHECK_MSG( m_menuItem, FALSE, _T("invalid menu item") );
651
652 wxCHECK( IsCheckable(), FALSE ); // can't get state of uncheckable item!
653
654 bool bIsChecked = ((GtkCheckMenuItem*)m_menuItem)->active != 0;
655
656 return bIsChecked;
657 }
658
659 //-----------------------------------------------------------------------------
660 // wxMenu
661 //-----------------------------------------------------------------------------
662
663 IMPLEMENT_DYNAMIC_CLASS(wxMenu,wxEvtHandler)
664
665 void
666 wxMenu::Init( const wxString& title,
667 long style,
668 const wxFunction func
669 )
670 {
671 m_title = title;
672 m_items.DeleteContents( TRUE );
673 m_invokingWindow = (wxWindow *) NULL;
674 m_style = style;
675
676 #if (GTK_MINOR_VERSION > 0)
677 m_accel = gtk_accel_group_new();
678 m_factory = gtk_item_factory_new( GTK_TYPE_MENU, "<main>", m_accel );
679 m_menu = gtk_item_factory_get_widget( m_factory, "<main>" );
680 #else
681 m_menu = gtk_menu_new(); // Do not show!
682 #endif
683
684 m_callback = func;
685
686 m_eventHandler = this;
687 m_clientData = (void*) NULL;
688
689 if (m_title.IsNull()) m_title = _T("");
690 if (m_title != _T(""))
691 {
692 Append(-2, m_title);
693 AppendSeparator();
694 }
695
696 m_owner = (GtkWidget*) NULL;
697
698 #if (GTK_MINOR_VERSION > 0)
699 /* Tearoffs are entries, just like separators. So if we want this
700 menu to be a tear-off one, we just append a tearoff entry
701 immediately. */
702 if(m_style & wxMENU_TEAROFF)
703 {
704 GtkItemFactoryEntry entry;
705 entry.path = "/tearoff";
706 entry.callback = (GtkItemFactoryCallback) NULL;
707 entry.callback_action = 0;
708 entry.item_type = "<Tearoff>";
709 entry.accelerator = (gchar*) NULL;
710 gtk_item_factory_create_item( m_factory, &entry, (gpointer) this, 2 ); /* what is 2 ? */
711 //GtkWidget *menuItem = gtk_item_factory_get_widget( m_factory, "<main>/tearoff" );
712 }
713 #endif
714 }
715
716 wxMenu::~wxMenu()
717 {
718 /* how do we delete an item-factory ? */
719 gtk_widget_destroy( m_menu );
720
721 }
722
723 void wxMenu::SetTitle( const wxString& title )
724 {
725 // TODO Waiting for something better
726 m_title = title;
727 }
728
729 const wxString wxMenu::GetTitle() const
730 {
731 return m_title;
732 }
733
734 void wxMenu::AppendSeparator()
735 {
736 wxMenuItem *mitem = new wxMenuItem();
737 mitem->SetId(ID_SEPARATOR);
738
739 #if (GTK_MINOR_VERSION > 0)
740 GtkItemFactoryEntry entry;
741 entry.path = "/sep";
742 entry.callback = (GtkItemFactoryCallback) NULL;
743 entry.callback_action = 0;
744 entry.item_type = "<Separator>";
745 entry.accelerator = (gchar*) NULL;
746
747 gtk_item_factory_create_item( m_factory, &entry, (gpointer) this, 2 ); /* what is 2 ? */
748
749 /* this will be wrong for more than one separator. do we care? */
750 GtkWidget *menuItem = gtk_item_factory_get_widget( m_factory, "<main>/sep" );
751 #else
752 GtkWidget *menuItem = gtk_menu_item_new();
753 gtk_menu_append( GTK_MENU(m_menu), menuItem );
754 gtk_widget_show( menuItem );
755 #endif
756
757 mitem->SetMenuItem(menuItem);
758 m_items.Append( mitem );
759 }
760
761 #if (GTK_MINOR_VERSION > 0)
762 static char* GetHotKey( const wxString &hotkey, char *hotbuf )
763 {
764 if (hotkey.IsEmpty()) return (char*) NULL;
765
766 switch (hotkey[0])
767 {
768 case _T('a'): /* Alt */
769 case _T('A'):
770 case _T('m'): /* Meta */
771 case _T('M'):
772 {
773 strcpy( hotbuf, "<alt>" );
774 wxString last = hotkey.Right(1);
775 strcat( hotbuf, last.mb_str() );
776 return hotbuf;
777 }
778 case _T('c'): /* Ctrl */
779 case _T('C'):
780 case _T('s'): /* Strg, yeah man, I'm German */
781 case _T('S'):
782 {
783 strcpy( hotbuf, "<control>" );
784 wxString last = hotkey.Right(1);
785 strcat( hotbuf, last.mb_str() );
786 return hotbuf;
787 }
788 case _T('F'): /* function keys */
789 {
790 strcpy( hotbuf, hotkey.mb_str() );
791 return hotbuf;
792 }
793 default:
794 {
795 }
796 }
797 return (char*) NULL;
798 }
799 #endif
800
801 void wxMenu::Append( int id, const wxString &item, const wxString &helpStr, bool checkable )
802 {
803 wxMenuItem *mitem = new wxMenuItem();
804 mitem->SetId(id);
805 mitem->SetText(item);
806 mitem->SetHelp(helpStr);
807 mitem->SetCheckable(checkable);
808
809 #if (GTK_MINOR_VERSION > 0)
810 /* text has "_" instead of "&" after mitem->SetText() */
811 wxString text( mitem->GetText() );
812
813 /* local buffer in multibyte form */
814 char buf[200];
815 strcpy( buf, "/" );
816 strcat( buf, text.mb_str() );
817
818 GtkItemFactoryEntry entry;
819 entry.path = buf;
820 entry.callback = (GtkItemFactoryCallback) gtk_menu_clicked_callback;
821 entry.callback_action = 0;
822 if (checkable)
823 entry.item_type = "<CheckItem>";
824 else
825 entry.item_type = "<Item>";
826
827 char hotbuf[50];
828 entry.accelerator = GetHotKey( mitem->GetHotKey(), hotbuf );
829
830 gtk_item_factory_create_item( m_factory, &entry, (gpointer) this, 2 ); /* what is 2 ? */
831
832 /* in order to get the pointer to the item we need the item text _without_ underscores */
833 wxString s = _T("<main>/");
834 for ( const wxChar *pc = text; *pc != _T('\0'); pc++ )
835 {
836 while (*pc == _T('_')) pc++; /* skip it */
837 s << *pc;
838 }
839
840 GtkWidget *menuItem = gtk_item_factory_get_widget( m_factory, s.mb_str() );
841
842 #else
843
844 GtkWidget *menuItem = checkable ? gtk_check_menu_item_new_with_label( mitem->GetText().mb_str() )
845 : gtk_menu_item_new_with_label( mitem->GetText().mb_str() );
846
847 gtk_signal_connect( GTK_OBJECT(menuItem), "activate",
848 GTK_SIGNAL_FUNC(gtk_menu_clicked_callback),
849 (gpointer)this );
850
851 gtk_menu_append( GTK_MENU(m_menu), menuItem );
852 gtk_widget_show( menuItem );
853
854 #endif
855
856 gtk_signal_connect( GTK_OBJECT(menuItem), "select",
857 GTK_SIGNAL_FUNC(gtk_menu_hilight_callback),
858 (gpointer)this );
859
860 gtk_signal_connect( GTK_OBJECT(menuItem), "deselect",
861 GTK_SIGNAL_FUNC(gtk_menu_nolight_callback),
862 (gpointer)this );
863
864 mitem->SetMenuItem(menuItem);
865
866 m_items.Append( mitem );
867 }
868
869 void wxMenu::Append( int id, const wxString &item, wxMenu *subMenu, const wxString &helpStr )
870 {
871 wxMenuItem *mitem = new wxMenuItem();
872 mitem->SetId(id);
873 mitem->SetText(item);
874 mitem->SetHelp(helpStr);
875
876 #if (GTK_MINOR_VERSION > 0)
877 /* text has "_" instead of "&" after mitem->SetText() */
878 wxString text( mitem->GetText() );
879
880 /* local buffer in multibyte form */
881 char buf[200];
882 strcpy( buf, "/" );
883 strcat( buf, text.mb_str() );
884
885 GtkItemFactoryEntry entry;
886 entry.path = buf;
887 entry.callback = (GtkItemFactoryCallback) 0;
888 entry.callback_action = 0;
889 entry.item_type = "<Branch>";
890
891 gtk_item_factory_create_item( m_factory, &entry, (gpointer) this, 2 ); /* what is 2 ? */
892
893 /* in order to get the pointer to the item we need the item text _without_ underscores */
894 wxString s = _T("<main>/");
895 for ( const wxChar *pc = text; *pc != _T('\0'); pc++ )
896 {
897 if (*pc == _T('_')) pc++; /* skip it */
898 s << *pc;
899 }
900
901 GtkWidget *menuItem = gtk_item_factory_get_item( m_factory, s.mb_str() );
902
903 #else
904
905 GtkWidget *menuItem = gtk_menu_item_new_with_label(mitem->GetText().mbc_str());
906
907 gtk_menu_append( GTK_MENU(m_menu), menuItem );
908 gtk_widget_show( menuItem );
909
910 #endif
911
912 gtk_signal_connect( GTK_OBJECT(menuItem), "select",
913 GTK_SIGNAL_FUNC(gtk_menu_hilight_callback),
914 (gpointer*)this );
915
916 gtk_signal_connect( GTK_OBJECT(menuItem), "deselect",
917 GTK_SIGNAL_FUNC(gtk_menu_nolight_callback),
918 (gpointer*)this );
919
920 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menuItem), subMenu->m_menu );
921
922 mitem->SetMenuItem(menuItem);
923 mitem->SetSubMenu(subMenu);
924
925 m_items.Append( mitem );
926 }
927
928 void wxMenu::Append( wxMenuItem *item )
929 {
930 m_items.Append( item );
931
932 GtkWidget *menuItem = (GtkWidget*) NULL;
933
934 if (item->IsSeparator())
935 menuItem = gtk_menu_item_new();
936 else if (item->IsSubMenu())
937 menuItem = gtk_menu_item_new_with_label(item->GetText().mbc_str());
938 else
939 menuItem = item->IsCheckable() ? gtk_check_menu_item_new_with_label(item->GetText().mbc_str())
940 : gtk_menu_item_new_with_label(item->GetText().mbc_str());
941
942 if (!item->IsSeparator())
943 {
944 gtk_signal_connect( GTK_OBJECT(menuItem), "select",
945 GTK_SIGNAL_FUNC(gtk_menu_hilight_callback),
946 (gpointer*)this );
947
948 gtk_signal_connect( GTK_OBJECT(menuItem), "deselect",
949 GTK_SIGNAL_FUNC(gtk_menu_nolight_callback),
950 (gpointer*)this );
951
952 if (!item->IsSubMenu())
953 {
954 gtk_signal_connect( GTK_OBJECT(menuItem), "activate",
955 GTK_SIGNAL_FUNC(gtk_menu_clicked_callback),
956 (gpointer*)this );
957 }
958 }
959
960 gtk_menu_append( GTK_MENU(m_menu), menuItem );
961 gtk_widget_show( menuItem );
962 item->SetMenuItem(menuItem);
963 }
964
965 int wxMenu::FindItem( const wxString itemString ) const
966 {
967 wxString s = _T("");
968 for ( const wxChar *pc = itemString; *pc != _T('\0'); pc++ )
969 {
970 if (*pc == _T('&'))
971 {
972 pc++; /* skip it */
973 #if (GTK_MINOR_VERSION > 0)
974 s << _T('_');
975 #endif
976 }
977 s << *pc;
978 }
979
980 wxNode *node = m_items.First();
981 while (node)
982 {
983 wxMenuItem *item = (wxMenuItem*)node->Data();
984 if (item->GetText() == s)
985 {
986 return item->GetId();
987 }
988 node = node->Next();
989 }
990
991 return wxNOT_FOUND;
992 }
993
994 void wxMenu::Enable( int id, bool enable )
995 {
996 wxMenuItem *item = FindItem(id);
997
998 wxCHECK_RET( item, _T("wxMenu::Enable: no such item") );
999
1000 item->Enable(enable);
1001 }
1002
1003 bool wxMenu::IsEnabled( int id ) const
1004 {
1005 wxMenuItem *item = FindItem(id);
1006
1007 wxCHECK_MSG( item, FALSE, _T("wxMenu::IsEnabled: no such item") );
1008
1009 return item->IsEnabled();
1010 }
1011
1012 void wxMenu::Check( int id, bool enable )
1013 {
1014 wxMenuItem *item = FindItem(id);
1015
1016 wxCHECK_RET( item, _T("wxMenu::Check: no such item") );
1017
1018 item->Check(enable);
1019 }
1020
1021 bool wxMenu::IsChecked( int id ) const
1022 {
1023 wxMenuItem *item = FindItem(id);
1024
1025 wxCHECK_MSG( item, FALSE, _T("wxMenu::IsChecked: no such item") );
1026
1027 return item->IsChecked();
1028 }
1029
1030 void wxMenu::SetLabel( int id, const wxString &label )
1031 {
1032 wxMenuItem *item = FindItem(id);
1033
1034 wxCHECK_RET( item, _T("wxMenu::SetLabel: no such item") );
1035
1036 item->SetText(label);
1037 }
1038
1039 wxString wxMenu::GetLabel( int id ) const
1040 {
1041 wxMenuItem *item = FindItem(id);
1042
1043 wxCHECK_MSG( item, _T(""), _T("wxMenu::GetLabel: no such item") );
1044
1045 return item->GetText();
1046 }
1047
1048 void wxMenu::SetHelpString( int id, const wxString& helpString )
1049 {
1050 wxMenuItem *item = FindItem(id);
1051
1052 wxCHECK_RET( item, _T("wxMenu::SetHelpString: no such item") );
1053
1054 item->SetHelp( helpString );
1055 }
1056
1057 wxString wxMenu::GetHelpString( int id ) const
1058 {
1059 wxMenuItem *item = FindItem(id);
1060
1061 wxCHECK_MSG( item, _T(""), _T("wxMenu::GetHelpString: no such item") );
1062
1063 return item->GetHelp();
1064 }
1065
1066 int wxMenu::FindMenuIdByMenuItem( GtkWidget *menuItem ) const
1067 {
1068 wxNode *node = m_items.First();
1069 while (node)
1070 {
1071 wxMenuItem *item = (wxMenuItem*)node->Data();
1072 if (item->GetMenuItem() == menuItem)
1073 return item->GetId();
1074 node = node->Next();
1075 }
1076
1077 return wxNOT_FOUND;
1078 }
1079
1080 wxMenuItem *wxMenu::FindItem(int id) const
1081 {
1082 wxNode *node = m_items.First();
1083 while (node)
1084 {
1085 wxMenuItem *item = (wxMenuItem*)node->Data();
1086 if (item->GetId() == id)
1087 {
1088 return item;
1089 }
1090 node = node->Next();
1091 }
1092
1093 /* Not finding anything here can be correct
1094 * when search the entire menu system for
1095 * an entry -> no error message. */
1096
1097 return (wxMenuItem *) NULL;
1098 }
1099
1100 void wxMenu::SetInvokingWindow( wxWindow *win )
1101 {
1102 m_invokingWindow = win;
1103 }
1104
1105 wxWindow *wxMenu::GetInvokingWindow()
1106 {
1107 return m_invokingWindow;
1108 }
1109
1110 // Update a menu and all submenus recursively. source is the object that has
1111 // the update event handlers defined for it. If NULL, the menu or associated
1112 // window will be used.
1113 void wxMenu::UpdateUI(wxEvtHandler* source)
1114 {
1115 if (!source && GetInvokingWindow())
1116 source = GetInvokingWindow()->GetEventHandler();
1117 if (!source)
1118 source = GetEventHandler();
1119 if (!source)
1120 source = this;
1121
1122 wxNode* node = GetItems().First();
1123 while (node)
1124 {
1125 wxMenuItem* item = (wxMenuItem*) node->Data();
1126 if ( !item->IsSeparator() )
1127 {
1128 wxWindowID id = item->GetId();
1129 wxUpdateUIEvent event(id);
1130 event.SetEventObject( source );
1131
1132 if (source->ProcessEvent(event))
1133 {
1134 if (event.GetSetText())
1135 SetLabel(id, event.GetText());
1136 if (event.GetSetChecked())
1137 Check(id, event.GetChecked());
1138 if (event.GetSetEnabled())
1139 Enable(id, event.GetEnabled());
1140 }
1141
1142 if (item->GetSubMenu())
1143 item->GetSubMenu()->UpdateUI(source);
1144 }
1145 node = node->Next();
1146 }
1147 }
1148