]> git.saurik.com Git - wxWidgets.git/blob - src/gtk/menu.cpp
wxMenu code clean up
[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 // wxMenuBar
25 //-----------------------------------------------------------------------------
26
27 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar,wxWindow)
28
29 wxMenuBar::wxMenuBar( long style )
30 {
31 m_needParent = FALSE; // hmmm
32
33 PreCreation( (wxWindow *) NULL, -1, wxDefaultPosition, wxDefaultSize, 0, "menu" );
34
35 m_menus.DeleteContents( TRUE );
36
37 m_menubar = gtk_menu_bar_new();
38
39 if (style & wxMB_DOCKABLE)
40 {
41 m_widget = gtk_handle_box_new();
42 gtk_container_add( GTK_CONTAINER(m_widget), GTK_WIDGET(m_menubar) );
43 gtk_widget_show( GTK_WIDGET(m_menubar) );
44 }
45 else
46 {
47 m_widget = GTK_WIDGET(m_menubar);
48 }
49
50 PostCreation();
51
52 Show( TRUE );
53 }
54
55 wxMenuBar::wxMenuBar()
56 {
57 m_needParent = FALSE; // hmmm
58
59 PreCreation( (wxWindow *) NULL, -1, wxDefaultPosition, wxDefaultSize, 0, "menu" );
60
61 m_menus.DeleteContents( TRUE );
62
63 m_menubar = gtk_menu_bar_new();
64
65 m_widget = GTK_WIDGET(m_menubar);
66
67 PostCreation();
68
69 Show( TRUE );
70 }
71
72 void wxMenuBar::Append( wxMenu *menu, const wxString &title )
73 {
74 m_menus.Append( menu );
75 wxString title2 = title;
76
77 int pos;
78 do
79 {
80 pos = title2.First( '&' );
81 if (pos != wxNOT_FOUND)
82 title2.Remove( pos, 1 );
83 } while (pos != wxNOT_FOUND);
84
85 menu->SetTitle(title2);
86
87 menu->m_owner = gtk_menu_item_new_with_label( WXSTRINGCAST(title2) );
88 gtk_widget_show( menu->m_owner );
89 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu->m_owner), menu->m_menu );
90
91 gtk_menu_bar_append( GTK_MENU_BAR(m_menubar), menu->m_owner );
92 }
93
94 static int FindMenuItemRecursive( const wxMenu *menu, const wxString &menuString, const wxString &itemString )
95 {
96 if (menu->GetTitle() == menuString)
97 {
98 int res = menu->FindItem( itemString );
99 if (res != wxNOT_FOUND)
100 return res;
101 }
102
103 wxNode *node = ((wxMenu *)menu)->GetItems().First(); // const_cast
104 while (node)
105 {
106 wxMenuItem *item = (wxMenuItem*)node->Data();
107 if (item->IsSubMenu())
108 return FindMenuItemRecursive(item->GetSubMenu(), menuString, itemString);
109
110 node = node->Next();
111 }
112
113 return wxNOT_FOUND;
114 }
115
116 wxMenuItem *wxMenuBar::FindItemForId(int itemId, wxMenu **menuForItem = NULL) const
117 {
118 if ( menuForItem )
119 {
120 // TODO return the pointer to the menu
121
122 *menuForItem = NULL;
123 }
124
125 return FindItem(itemId);
126 }
127
128 int wxMenuBar::FindMenuItem( const wxString &menuString, const wxString &itemString ) const
129 {
130 wxNode *node = m_menus.First();
131 while (node)
132 {
133 wxMenu *menu = (wxMenu*)node->Data();
134 int res = FindMenuItemRecursive( menu, menuString, itemString);
135 if (res != -1) return res;
136 node = node->Next();
137 }
138 return -1;
139 }
140
141 // Find a wxMenuItem using its id. Recurses down into sub-menus
142 static wxMenuItem* FindMenuItemByIdRecursive(const wxMenu* menu, int id)
143 {
144 wxMenuItem* result = menu->FindItem(id);
145
146 wxNode *node = ((wxMenu *)menu)->GetItems().First(); // const_cast
147 while ( node && result == NULL )
148 {
149 wxMenuItem *item = (wxMenuItem*)node->Data();
150 if (item->IsSubMenu())
151 {
152 result = FindMenuItemByIdRecursive( item->GetSubMenu(), id );
153 }
154 node = node->Next();
155 }
156
157 return result;
158 }
159
160 wxMenuItem* wxMenuBar::FindItem( int id ) const
161 {
162 wxMenuItem* result = 0;
163 wxNode *node = m_menus.First();
164 while (node && result == 0)
165 {
166 wxMenu *menu = (wxMenu*)node->Data();
167 result = FindMenuItemByIdRecursive( menu, id );
168 node = node->Next();
169 }
170
171 return result;
172 }
173
174 void wxMenuBar::Check( int id, bool check )
175 {
176 wxMenuItem* item = FindMenuItemById( id );
177
178 wxCHECK_RET( item, "wxMenuBar::Check: no such item" );
179
180 item->Check(check);
181 }
182
183 bool wxMenuBar::IsChecked( int id ) const
184 {
185 wxMenuItem* item = FindMenuItemById( id );
186
187 wxCHECK_MSG( item, FALSE, "wxMenuBar::IsChecked: no such item" );
188
189 return item->IsChecked();
190 }
191
192 void wxMenuBar::Enable( int id, bool enable )
193 {
194 wxMenuItem* item = FindMenuItemById( id );
195
196 wxCHECK_RET( item, "wxMenuBar::Enable: no such item" );
197
198 item->Enable(enable);
199 }
200
201 bool wxMenuBar::IsEnabled( int id ) const
202 {
203 wxMenuItem* item = FindMenuItemById( id );
204
205 wxCHECK_MSG( item, FALSE, "wxMenuBar::IsEnabled: no such item" );
206
207 return item->IsEnabled();
208 }
209
210 wxString wxMenuBar::GetLabel( int id ) const
211 {
212 wxMenuItem* item = FindMenuItemById( id );
213
214 wxCHECK_MSG( item, "", "wxMenuBar::GetLabel: no such item" );
215
216 return item->GetText();
217 }
218
219 void wxMenuBar::SetLabel( int id, const wxString &label )
220 {
221 wxMenuItem* item = FindMenuItemById( id );
222
223 wxCHECK_RET( item, "wxMenuBar::SetLabel: no such item" );
224
225 item->SetText( label );
226 }
227
228 void wxMenuBar::EnableTop( int pos, bool flag )
229 {
230 wxNode *node = m_menus.Nth( pos );
231
232 wxCHECK_RET( node, "menu not found" );
233
234 wxMenu* menu = (wxMenu*)node->Data();
235
236 if (menu->m_owner)
237 gtk_widget_set_sensitive( menu->m_owner, flag );
238 }
239
240 wxString wxMenuBar::GetLabelTop( int pos ) const
241 {
242 wxNode *node = m_menus.Nth( pos );
243
244 wxCHECK_MSG( node, "invalid", "menu not found" );
245
246 wxMenu* menu = (wxMenu*)node->Data();
247
248 return menu->GetTitle();
249 }
250
251 void wxMenuBar::SetLabelTop( int pos, const wxString& label )
252 {
253 wxNode *node = m_menus.Nth( pos );
254
255 wxCHECK_RET( node, "menu not found" );
256
257 wxMenu* menu = (wxMenu*)node->Data();
258
259 menu->SetTitle( label );
260 }
261
262 void wxMenuBar::SetHelpString( int id, const wxString& helpString )
263 {
264 wxMenuItem* item = FindMenuItemById( id );
265
266 wxCHECK_RET( item, "wxMenuBar::SetHelpString: no such item" );
267
268 item->SetHelp( helpString );
269 }
270
271 wxString wxMenuBar::GetHelpString( int id ) const
272 {
273 wxMenuItem* item = FindMenuItemById( id );
274
275 wxCHECK_MSG( item, "", "wxMenuBar::GetHelpString: no such item" );
276
277 return item->GetHelp();
278 }
279
280 //-----------------------------------------------------------------------------
281 // "activate"
282 //-----------------------------------------------------------------------------
283
284 static void gtk_menu_clicked_callback( GtkWidget *widget, wxMenu *menu )
285 {
286 int id = menu->FindMenuIdByMenuItem(widget);
287
288 /* should find it for normal (not popup) menu */
289 wxASSERT( (id != -1) || (menu->GetInvokingWindow() != NULL) );
290
291 if (!menu->IsEnabled(id))
292 return;
293
294 wxMenuItem* item = menu->FindItem( id );
295 wxCHECK_RET( item, "error in menu item callback" );
296
297 if (item->IsCheckable())
298 {
299 if (item->GetCheckedFlag() == item->IsChecked())
300 {
301 /* the menu item has been checked by calling wxMenuItem->Check() */
302 return;
303 }
304 else
305 {
306 /* the user pressed on the menu item -> report */
307 item->SetCheckedFlag(item->IsChecked()); /* make consistent again */
308 }
309 }
310
311 wxCommandEvent event( wxEVT_COMMAND_MENU_SELECTED, id );
312 event.SetEventObject( menu );
313 event.SetInt(id );
314
315 if (menu->GetCallback())
316 {
317 (void) (*(menu->GetCallback())) (*menu, event);
318 return;
319 }
320
321 if (menu->GetEventHandler()->ProcessEvent(event))
322 return;
323
324 wxWindow *win = menu->GetInvokingWindow();
325 if (win)
326 win->GetEventHandler()->ProcessEvent( event );
327 }
328
329 //-----------------------------------------------------------------------------
330 // "select"
331 //-----------------------------------------------------------------------------
332
333 static void gtk_menu_hilight_callback( GtkWidget *widget, wxMenu *menu )
334 {
335 int id = menu->FindMenuIdByMenuItem(widget);
336
337 wxASSERT( id != -1 ); // should find it!
338
339 if (!menu->IsEnabled(id))
340 return;
341
342 wxMenuEvent event( wxEVT_MENU_HIGHLIGHT, id );
343 event.SetEventObject( menu );
344
345 /* wxMSW doesn't call callback here either
346
347 if (menu->m_callback)
348 {
349 (void) (*(menu->m_callback)) (*menu, event);
350 return;
351 }
352 */
353
354 if (menu->GetEventHandler()->ProcessEvent(event))
355 return;
356
357 wxWindow *win = menu->GetInvokingWindow();
358 if (win) win->GetEventHandler()->ProcessEvent( event );
359 }
360
361 //-----------------------------------------------------------------------------
362 // "deselect"
363 //-----------------------------------------------------------------------------
364
365 static void gtk_menu_nolight_callback( GtkWidget *widget, wxMenu *menu )
366 {
367 int id = menu->FindMenuIdByMenuItem(widget);
368
369 wxASSERT( id != -1 ); // should find it!
370
371 if (!menu->IsEnabled(id))
372 return;
373
374 wxMenuEvent event( wxEVT_MENU_HIGHLIGHT, -1 );
375 event.SetEventObject( menu );
376
377 if (menu->GetEventHandler()->ProcessEvent(event))
378 return;
379
380 wxWindow *win = menu->GetInvokingWindow();
381 if (win)
382 win->GetEventHandler()->ProcessEvent( event );
383 }
384
385 //-----------------------------------------------------------------------------
386 // wxMenuItem
387 //-----------------------------------------------------------------------------
388
389 IMPLEMENT_DYNAMIC_CLASS(wxMenuItem,wxObject)
390
391 wxMenuItem::wxMenuItem()
392 {
393 m_id = ID_SEPARATOR;
394 m_isCheckMenu = FALSE;
395 m_isChecked = FALSE;
396 m_isEnabled = TRUE;
397 m_subMenu = (wxMenu *) NULL;
398 m_menuItem = (GtkWidget *) NULL;
399 }
400
401 // it's valid for this function to be called even if m_menuItem == NULL
402 void wxMenuItem::SetName( const wxString& str )
403 {
404 m_text = "";
405 for ( const char *pc = str; *pc != '\0'; pc++ )
406 {
407 if (*pc == '&') pc++; /* skip it */
408 m_text << *pc;
409 }
410
411 if (m_menuItem)
412 {
413 GtkLabel *label = GTK_LABEL( GTK_BIN(m_menuItem)->child );
414 gtk_label_set( label, m_text.c_str());
415 }
416 }
417
418 void wxMenuItem::Check( bool check )
419 {
420 wxCHECK_RET( m_menuItem, "invalid menu item" );
421
422 wxCHECK_RET( IsCheckable(), "Can't check uncheckable item!" )
423
424 if (check == m_isChecked) return;
425
426 m_isChecked = check;
427 gtk_check_menu_item_set_state( (GtkCheckMenuItem*)m_menuItem, (gint)check );
428 }
429
430 void wxMenuItem::Enable( bool enable )
431 {
432 wxCHECK_RET( m_menuItem, "invalid menu item" );
433
434 gtk_widget_set_sensitive( m_menuItem, enable );
435 m_isEnabled = enable;
436 }
437
438 bool wxMenuItem::IsChecked() const
439 {
440 wxCHECK_MSG( m_menuItem, FALSE, "invalid menu item" );
441
442 wxCHECK( IsCheckable(), FALSE ); // can't get state of uncheckable item!
443
444 bool bIsChecked = ((GtkCheckMenuItem*)m_menuItem)->active != 0;
445
446 return bIsChecked;
447 }
448
449 //-----------------------------------------------------------------------------
450 // wxMenu
451 //-----------------------------------------------------------------------------
452
453 IMPLEMENT_DYNAMIC_CLASS(wxMenu,wxEvtHandler)
454
455 wxMenu::wxMenu( const wxString& title, const wxFunction func )
456 {
457 m_title = title;
458 m_items.DeleteContents( TRUE );
459 m_invokingWindow = (wxWindow *) NULL;
460 m_menu = gtk_menu_new(); // Do not show!
461
462 m_callback = func;
463 m_eventHandler = this;
464 m_clientData = (void*) NULL;
465
466 if (m_title.IsNull()) m_title = "";
467 if (m_title != "")
468 {
469 Append(-2, m_title);
470 AppendSeparator();
471 }
472
473 m_owner = (GtkWidget*) NULL;
474 }
475
476 void wxMenu::SetTitle( const wxString& title )
477 {
478 // TODO Waiting for something better
479 m_title = title;
480 }
481
482 const wxString wxMenu::GetTitle() const
483 {
484 return m_title;
485 }
486
487 void wxMenu::AppendSeparator()
488 {
489 wxMenuItem *mitem = new wxMenuItem();
490 mitem->SetId(ID_SEPARATOR);
491
492 GtkWidget *menuItem = gtk_menu_item_new();
493 gtk_menu_append( GTK_MENU(m_menu), menuItem );
494 gtk_widget_show( menuItem );
495 mitem->SetMenuItem(menuItem);
496 m_items.Append( mitem );
497 }
498
499 void wxMenu::Append( int id, const wxString &item, const wxString &helpStr, bool checkable )
500 {
501 wxMenuItem *mitem = new wxMenuItem();
502 mitem->SetId(id);
503 mitem->SetText(item);
504 mitem->SetHelp(helpStr);
505 mitem->SetCheckable(checkable);
506 const char *text = mitem->GetText();
507 GtkWidget *menuItem = checkable ? gtk_check_menu_item_new_with_label(text)
508 : gtk_menu_item_new_with_label(text);
509
510 mitem->SetMenuItem(menuItem);
511
512 gtk_signal_connect( GTK_OBJECT(menuItem), "activate",
513 GTK_SIGNAL_FUNC(gtk_menu_clicked_callback),
514 (gpointer*)this );
515
516 gtk_signal_connect( GTK_OBJECT(menuItem), "select",
517 GTK_SIGNAL_FUNC(gtk_menu_hilight_callback),
518 (gpointer*)this );
519
520 gtk_signal_connect( GTK_OBJECT(menuItem), "deselect",
521 GTK_SIGNAL_FUNC(gtk_menu_nolight_callback),
522 (gpointer*)this );
523
524 gtk_menu_append( GTK_MENU(m_menu), menuItem );
525 gtk_widget_show( menuItem );
526 m_items.Append( mitem );
527 }
528
529 void wxMenu::Append( int id, const wxString &text, wxMenu *subMenu, const wxString &helpStr )
530 {
531 wxMenuItem *mitem = new wxMenuItem();
532 mitem->SetId(id);
533 mitem->SetText(text);
534 mitem->SetHelp(helpStr);
535
536 GtkWidget *menuItem = gtk_menu_item_new_with_label(mitem->GetText());
537 mitem->SetMenuItem(menuItem);
538 mitem->SetSubMenu(subMenu);
539
540 gtk_signal_connect( GTK_OBJECT(menuItem), "select",
541 GTK_SIGNAL_FUNC(gtk_menu_hilight_callback),
542 (gpointer*)this );
543
544 gtk_signal_connect( GTK_OBJECT(menuItem), "deselect",
545 GTK_SIGNAL_FUNC(gtk_menu_nolight_callback),
546 (gpointer*)this );
547
548 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menuItem), subMenu->m_menu );
549 gtk_menu_append( GTK_MENU(m_menu), menuItem );
550 gtk_widget_show( menuItem );
551 m_items.Append( mitem );
552 }
553
554 void wxMenu::Append( wxMenuItem *item )
555 {
556 m_items.Append( item );
557
558 GtkWidget *menuItem = (GtkWidget*) NULL;
559
560 if (item->IsSeparator())
561 menuItem = gtk_menu_item_new();
562 else if (item->IsSubMenu())
563 menuItem = gtk_menu_item_new_with_label(item->GetText());
564 else
565 menuItem = item->IsCheckable() ? gtk_check_menu_item_new_with_label(item->GetText())
566 : gtk_menu_item_new_with_label(item->GetText());
567
568 if (!item->IsSeparator())
569 {
570 gtk_signal_connect( GTK_OBJECT(menuItem), "select",
571 GTK_SIGNAL_FUNC(gtk_menu_hilight_callback),
572 (gpointer*)this );
573
574 gtk_signal_connect( GTK_OBJECT(menuItem), "deselect",
575 GTK_SIGNAL_FUNC(gtk_menu_nolight_callback),
576 (gpointer*)this );
577
578 if (!item->IsSubMenu())
579 {
580 gtk_signal_connect( GTK_OBJECT(menuItem), "activate",
581 GTK_SIGNAL_FUNC(gtk_menu_clicked_callback),
582 (gpointer*)this );
583 }
584 }
585
586 gtk_menu_append( GTK_MENU(m_menu), menuItem );
587 gtk_widget_show( menuItem );
588 item->SetMenuItem(menuItem);
589 }
590
591 int wxMenu::FindItem( const wxString itemString ) const
592 {
593 wxString s( itemString );
594
595 int pos;
596 do
597 {
598 pos = s.First( '&' );
599 if (pos != -1) s.Remove( pos, 1 );
600 } while (pos != -1);
601
602 wxNode *node = m_items.First();
603 while (node)
604 {
605 wxMenuItem *item = (wxMenuItem*)node->Data();
606 if (item->GetText() == s)
607 {
608 return item->GetId();
609 }
610 node = node->Next();
611 }
612
613 return wxNOT_FOUND;
614 }
615
616 void wxMenu::Enable( int id, bool enable )
617 {
618 wxMenuItem *item = FindItem(id);
619
620 wxCHECK_RET( item, "wxMenu::Enable: no such item" );
621
622 item->Enable(enable);
623 }
624
625 bool wxMenu::IsEnabled( int id ) const
626 {
627 wxMenuItem *item = FindItem(id);
628
629 wxCHECK_MSG( item, FALSE, "wxMenu::IsEnabled: no such item" );
630
631 return item->IsEnabled();
632 }
633
634 void wxMenu::Check( int id, bool enable )
635 {
636 wxMenuItem *item = FindItem(id);
637
638 wxCHECK_RET( item, "wxMenu::Check: no such item" );
639
640 item->Check(enable);
641 }
642
643 bool wxMenu::IsChecked( int id ) const
644 {
645 wxMenuItem *item = FindItem(id);
646
647 wxCHECK_MSG( item, FALSE, "wxMenu::IsChecked: no such item" );
648
649 return item->IsChecked();
650 }
651
652 void wxMenu::SetLabel( int id, const wxString &label )
653 {
654 wxMenuItem *item = FindItem(id);
655
656 wxCHECK_RET( item, "wxMenu::SetLabel: no such item" );
657
658 item->SetText(label);
659 }
660
661 wxString wxMenu::GetLabel( int id ) const
662 {
663 wxMenuItem *item = FindItem(id);
664
665 wxCHECK_MSG( item, "", "wxMenu::GetLabel: no such item" );
666
667 return item->GetText();
668 }
669
670 void wxMenu::SetHelpString( int id, const wxString& helpString )
671 {
672 wxMenuItem *item = FindItem(id);
673
674 wxCHECK_RET( item, "wxMenu::SetHelpString: no such item" );
675
676 item->SetHelp( helpString );
677 }
678
679 wxString wxMenu::GetHelpString( int id ) const
680 {
681 wxMenuItem *item = FindItem(id);
682
683 wxCHECK_MSG( item, "", "wxMenu::GetHelpString: no such item" );
684
685 return item->GetHelp();
686 }
687
688 int wxMenu::FindMenuIdByMenuItem( GtkWidget *menuItem ) const
689 {
690 wxNode *node = m_items.First();
691 while (node)
692 {
693 wxMenuItem *item = (wxMenuItem*)node->Data();
694 if (item->GetMenuItem() == menuItem)
695 return item->GetId();
696 node = node->Next();
697 }
698
699 return wxNOT_FOUND;
700 }
701
702 wxMenuItem *wxMenu::FindItem(int id) const
703 {
704 wxNode *node = m_items.First();
705 while (node)
706 {
707 wxMenuItem *item = (wxMenuItem*)node->Data();
708 if (item->GetId() == id)
709 {
710 return item;
711 }
712 node = node->Next();
713 }
714
715 /* Not finding anything here can be correct
716 * when search the entire menu system for
717 * an entry -> no error message. */
718
719 return (wxMenuItem *) NULL;
720 }
721
722 void wxMenu::SetInvokingWindow( wxWindow *win )
723 {
724 m_invokingWindow = win;
725 }
726
727 wxWindow *wxMenu::GetInvokingWindow()
728 {
729 return m_invokingWindow;
730 }
731
732 // Update a menu and all submenus recursively. source is the object that has
733 // the update event handlers defined for it. If NULL, the menu or associated
734 // window will be used.
735 void wxMenu::UpdateUI(wxEvtHandler* source)
736 {
737 if (!source && GetInvokingWindow())
738 source = GetInvokingWindow()->GetEventHandler();
739 if (!source)
740 source = GetEventHandler();
741 if (!source)
742 source = this;
743
744 wxNode* node = GetItems().First();
745 while (node)
746 {
747 wxMenuItem* item = (wxMenuItem*) node->Data();
748 if ( !item->IsSeparator() )
749 {
750 wxWindowID id = item->GetId();
751 wxUpdateUIEvent event(id);
752 event.SetEventObject( source );
753
754 if (source->ProcessEvent(event))
755 {
756 if (event.GetSetText())
757 SetLabel(id, event.GetText());
758 if (event.GetSetChecked())
759 Check(id, event.GetChecked());
760 if (event.GetSetEnabled())
761 Enable(id, event.GetEnabled());
762 }
763
764 if (item->GetSubMenu())
765 item->GetSubMenu()->UpdateUI(source);
766 }
767 node = node->Next();
768 }
769 }
770