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