]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/gtk1/menu.cpp
Fixed doubled-up key effects in wxTextCtrl by resetting m_lastMsg to 0
[wxWidgets.git] / src / gtk1 / menu.cpp
... / ...
CommitLineData
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
27IMPLEMENT_DYNAMIC_CLASS(wxMenuBar,wxWindow)
28
29wxMenuBar::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
55wxMenuBar::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
72void 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
94static 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
116wxMenuItem *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
128int 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
142static 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
160wxMenuItem* 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
174void 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
183bool 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
192void 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
201bool 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
210wxString 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
219void 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
228void 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
240wxString 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
251void 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
262void 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
271wxString 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
284static 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
333static 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
365static 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
389IMPLEMENT_DYNAMIC_CLASS(wxMenuItem,wxObject)
390
391wxMenuItem::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
402void 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
418void 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
430void 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
438bool 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
453IMPLEMENT_DYNAMIC_CLASS(wxMenu,wxEvtHandler)
454
455wxMenu::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
476void wxMenu::SetTitle( const wxString& title )
477{
478 // TODO Waiting for something better
479 m_title = title;
480}
481
482const wxString wxMenu::GetTitle() const
483{
484 return m_title;
485}
486
487void 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
499void 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
529void 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
554void 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
591int 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
616void 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
625bool 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
634void 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
643bool 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
652void 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
661wxString 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
670void 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
679wxString 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
688int 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
702wxMenuItem *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
722void wxMenu::SetInvokingWindow( wxWindow *win )
723{
724 m_invokingWindow = win;
725}
726
727wxWindow *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.
735void 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