1 /////////////////////////////////////////////////////////////////////////////
4 // Author: Robert Roebling
6 // Copyright: (c) 1998 Robert Roebling
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
11 #pragma implementation "menu.h"
12 #pragma implementation "menuitem.h"
23 //-----------------------------------------------------------------------------
25 //-----------------------------------------------------------------------------
27 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar
,wxWindow
)
29 wxMenuBar::wxMenuBar( long style
)
31 m_needParent
= FALSE
; // hmmm
33 PreCreation( (wxWindow
*) NULL
, -1, wxDefaultPosition
, wxDefaultSize
, 0, "menu" );
35 m_menus
.DeleteContents( TRUE
);
37 m_menubar
= gtk_menu_bar_new();
39 if (style
& wxMB_DOCKABLE
)
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
) );
47 m_widget
= GTK_WIDGET(m_menubar
);
55 wxMenuBar::wxMenuBar()
57 m_needParent
= FALSE
; // hmmm
59 PreCreation( (wxWindow
*) NULL
, -1, wxDefaultPosition
, wxDefaultSize
, 0, "menu" );
61 m_menus
.DeleteContents( TRUE
);
63 m_menubar
= gtk_menu_bar_new();
65 m_widget
= GTK_WIDGET(m_menubar
);
72 void wxMenuBar::Append( wxMenu
*menu
, const wxString
&title
)
74 m_menus
.Append( menu
);
75 menu
->m_title
= title
;
80 pos
= menu
->m_title
.First( '&' );
81 if (pos
!= -1) menu
->m_title
.Remove( pos
, 1 );
84 menu
->m_owner
= gtk_menu_item_new_with_label( WXSTRINGCAST(menu
->m_title
) );
85 gtk_widget_show( menu
->m_owner
);
86 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu
->m_owner
), menu
->m_menu
);
88 gtk_menu_bar_append( GTK_MENU_BAR(m_menubar
), menu
->m_owner
);
91 static int FindMenuItemRecursive( const wxMenu
*menu
, const wxString
&menuString
, const wxString
&itemString
)
93 if (menu
->m_title
== menuString
)
95 int res
= menu
->FindItem( itemString
);
96 if (res
!= -1) return res
;
99 wxNode
*node
= menu
->m_items
.First();
102 wxMenuItem
*item
= (wxMenuItem
*)node
->Data();
103 if (item
->IsSubMenu())
104 return FindMenuItemRecursive(item
->GetSubMenu(), menuString
, itemString
);
112 int wxMenuBar::FindMenuItem( const wxString
&menuString
, const wxString
&itemString
) const
114 wxNode
*node
= m_menus
.First();
117 wxMenu
*menu
= (wxMenu
*)node
->Data();
118 int res
= FindMenuItemRecursive( menu
, menuString
, itemString
);
119 if (res
!= -1) return res
;
125 /* Find a wxMenuItem using its id. Recurses down into sub-menus */
126 static wxMenuItem
* FindMenuItemByIdRecursive(const wxMenu
* menu
, int id
)
128 wxMenuItem
* result
= menu
->FindItem(id
);
130 wxNode
*node
= menu
->m_items
.First();
131 while ( node
&& result
== NULL
)
133 wxMenuItem
*item
= (wxMenuItem
*)node
->Data();
134 if (item
->IsSubMenu())
136 result
= FindMenuItemByIdRecursive( item
->GetSubMenu(), id
);
144 wxMenuItem
* wxMenuBar::FindMenuItemById( int id
) const
146 wxMenuItem
* result
= 0;
147 wxNode
*node
= m_menus
.First();
148 while (node
&& result
== 0)
150 wxMenu
*menu
= (wxMenu
*)node
->Data();
151 result
= FindMenuItemByIdRecursive( menu
, id
);
158 void wxMenuBar::Check( int id
, bool check
)
160 wxMenuItem
* item
= FindMenuItemById( id
);
161 if (item
) item
->Check(check
);
164 bool wxMenuBar::Checked( int id
) const
166 wxMenuItem
* item
= FindMenuItemById( id
);
167 if (item
) return item
->IsChecked();
171 void wxMenuBar::Enable( int id
, bool enable
)
173 wxMenuItem
* item
= FindMenuItemById( id
);
174 if (item
) item
->Enable(enable
);
177 bool wxMenuBar::Enabled( int id
) const
179 wxMenuItem
* item
= FindMenuItemById( id
);
180 if (item
) return item
->IsEnabled();
185 wxString
wxMenuBar::GetLabel( int id
) const
187 wxMenuItem
* item
= FindMenuItemById( id
);
189 if (item
) return item
->GetText();
194 void wxMenuBar::SetLabel( int id
, const wxString
&label
)
196 wxMenuItem
* item
= FindMenuItemById( id
);
198 if (item
) item
->SetText( label
);
201 void wxMenuBar::EnableTop( int pos
, bool flag
)
203 wxNode
*node
= m_menus
.Nth( pos
);
205 wxCHECK_RET( node
, "menu not found" );
207 wxMenu
* menu
= (wxMenu
*)node
->Data();
209 if (menu
->m_owner
) gtk_widget_set_sensitive( menu
->m_owner
, flag
);
212 wxString
wxMenuBar::GetLabelTop( int pos
) const
214 wxNode
*node
= m_menus
.Nth( pos
);
216 wxCHECK_MSG( node
, "invalid", "menu not found" );
218 wxMenu
* menu
= (wxMenu
*)node
->Data();
220 return menu
->GetTitle();
223 void wxMenuBar::SetLabelTop( int pos
, const wxString
& label
)
225 wxNode
*node
= m_menus
.Nth( pos
);
227 wxCHECK_RET( node
, "menu not found" );
229 wxMenu
* menu
= (wxMenu
*)node
->Data();
231 menu
->SetTitle( label
);
234 void wxMenuBar::SetHelpString( int id
, const wxString
& helpString
)
236 wxMenuItem
* item
= FindMenuItemById( id
);
238 if (item
) item
->SetHelp( helpString
);
241 wxString
wxMenuBar::GetHelpString( int id
) const
243 wxMenuItem
* item
= FindMenuItemById( id
);
246 return item
->GetHelp();
251 //-----------------------------------------------------------------------------
253 //-----------------------------------------------------------------------------
255 static void gtk_menu_clicked_callback( GtkWidget
*widget
, wxMenu
*menu
)
257 int id
= menu
->FindMenuIdByMenuItem(widget
);
259 /* should find it for normal (not popup) menu */
260 wxASSERT( (id
!= -1) || (menu
->GetInvokingWindow() != NULL
) );
262 if (!menu
->IsEnabled(id
)) return;
264 wxMenuItem
* item
= menu
->FindItem( id
);
265 wxCHECK_RET( item
, "error in menu item callback" );
267 if (item
->m_isCheckMenu
)
269 if (item
->m_isChecked
== item
->IsChecked())
271 /* the menu item has been checked by calling wxMenuItem->Check() */
276 /* the user pressed on the menu item -> report */
277 item
->m_isChecked
= item
->IsChecked(); /* make consistent again */
281 wxCommandEvent
event( wxEVT_COMMAND_MENU_SELECTED
, id
);
282 event
.SetEventObject( menu
);
285 if (menu
->m_callback
)
287 (void) (*(menu
->m_callback
)) (*menu
, event
);
291 if (menu
->GetEventHandler()->ProcessEvent(event
)) return;
293 wxWindow
*win
= menu
->GetInvokingWindow();
294 if (win
) win
->GetEventHandler()->ProcessEvent( event
);
297 //-----------------------------------------------------------------------------
299 //-----------------------------------------------------------------------------
301 static void gtk_menu_hilight_callback( GtkWidget
*widget
, wxMenu
*menu
)
303 int id
= menu
->FindMenuIdByMenuItem(widget
);
305 wxASSERT( id
!= -1 ); // should find it!
307 if (!menu
->IsEnabled(id
)) return;
309 wxMenuEvent
event( wxEVT_MENU_HIGHLIGHT
, id
);
310 event
.SetEventObject( menu
);
312 /* wxMSW doesn't call callback here either
314 if (menu->m_callback)
316 (void) (*(menu->m_callback)) (*menu, event);
321 if (menu
->GetEventHandler()->ProcessEvent(event
)) return;
323 wxWindow
*win
= menu
->GetInvokingWindow();
324 if (win
) win
->GetEventHandler()->ProcessEvent( event
);
327 //-----------------------------------------------------------------------------
329 //-----------------------------------------------------------------------------
331 static void gtk_menu_nolight_callback( GtkWidget
*widget
, wxMenu
*menu
)
333 int id
= menu
->FindMenuIdByMenuItem(widget
);
335 wxASSERT( id
!= -1 ); // should find it!
337 if (!menu
->IsEnabled(id
)) return;
339 wxMenuEvent
event( wxEVT_MENU_HIGHLIGHT
, -1 );
340 event
.SetEventObject( menu
);
342 if (menu
->GetEventHandler()->ProcessEvent(event
)) return;
344 wxWindow
*win
= menu
->GetInvokingWindow();
345 if (win
) win
->GetEventHandler()->ProcessEvent( event
);
348 //-----------------------------------------------------------------------------
350 //-----------------------------------------------------------------------------
352 IMPLEMENT_DYNAMIC_CLASS(wxMenuItem
,wxObject
)
354 wxMenuItem::wxMenuItem()
357 m_isCheckMenu
= FALSE
;
360 m_subMenu
= (wxMenu
*) NULL
;
361 m_menuItem
= (GtkWidget
*) NULL
;
364 /* it's valid for this function to be called even if m_menuItem == NULL */
365 void wxMenuItem::SetName( const wxString
& str
)
368 for ( const char *pc
= str
; *pc
!= '\0'; pc
++ )
370 if (*pc
== '&') pc
++; /* skip it */
376 GtkLabel
*label
= GTK_LABEL( GTK_BIN(m_menuItem
)->child
);
377 gtk_label_set( label
, m_text
.c_str());
381 void wxMenuItem::Check( bool check
)
383 wxCHECK_RET( m_menuItem
, "invalid menu item" );
385 wxCHECK_RET( IsCheckable(), "Can't check uncheckable item!" )
387 if (check
== m_isChecked
) return;
390 gtk_check_menu_item_set_state( (GtkCheckMenuItem
*)m_menuItem
, (gint
)check
);
393 void wxMenuItem::Enable( bool enable
)
395 wxCHECK_RET( m_menuItem
, "invalid menu item" );
397 gtk_widget_set_sensitive( m_menuItem
, enable
);
398 m_isEnabled
= enable
;
401 bool wxMenuItem::IsChecked() const
403 wxCHECK_MSG( m_menuItem
, FALSE
, "invalid menu item" );
405 wxCHECK( IsCheckable(), FALSE
); // can't get state of uncheckable item!
407 bool bIsChecked
= ((GtkCheckMenuItem
*)m_menuItem
)->active
!= 0;
412 //-----------------------------------------------------------------------------
414 //-----------------------------------------------------------------------------
416 IMPLEMENT_DYNAMIC_CLASS(wxMenu
,wxEvtHandler
)
418 wxMenu::wxMenu( const wxString
& title
, const wxFunction func
)
421 m_items
.DeleteContents( TRUE
);
422 m_invokingWindow
= (wxWindow
*) NULL
;
423 m_menu
= gtk_menu_new(); // Do not show!
426 m_eventHandler
= this;
427 m_clientData
= (void*) NULL
;
429 if (m_title
.IsNull()) m_title
= "";
436 m_owner
= (GtkWidget
*) NULL
;
439 void wxMenu::SetTitle( const wxString
& title
)
441 /* Waiting for something better. */
445 const wxString
wxMenu::GetTitle() const
450 void wxMenu::AppendSeparator()
452 wxMenuItem
*mitem
= new wxMenuItem();
453 mitem
->SetId(ID_SEPARATOR
);
455 GtkWidget
*menuItem
= gtk_menu_item_new();
456 gtk_menu_append( GTK_MENU(m_menu
), menuItem
);
457 gtk_widget_show( menuItem
);
458 mitem
->SetMenuItem(menuItem
);
459 m_items
.Append( mitem
);
462 void wxMenu::Append( int id
, const wxString
&item
, const wxString
&helpStr
, bool checkable
)
464 wxMenuItem
*mitem
= new wxMenuItem();
466 mitem
->SetText(item
);
467 mitem
->SetHelp(helpStr
);
468 mitem
->SetCheckable(checkable
);
469 const char *text
= mitem
->GetText();
470 GtkWidget
*menuItem
= checkable
? gtk_check_menu_item_new_with_label(text
)
471 : gtk_menu_item_new_with_label(text
);
473 mitem
->SetMenuItem(menuItem
);
475 gtk_signal_connect( GTK_OBJECT(menuItem
), "activate",
476 GTK_SIGNAL_FUNC(gtk_menu_clicked_callback
),
479 gtk_signal_connect( GTK_OBJECT(menuItem
), "select",
480 GTK_SIGNAL_FUNC(gtk_menu_hilight_callback
),
483 gtk_signal_connect( GTK_OBJECT(menuItem
), "deselect",
484 GTK_SIGNAL_FUNC(gtk_menu_nolight_callback
),
487 gtk_menu_append( GTK_MENU(m_menu
), menuItem
);
488 gtk_widget_show( menuItem
);
489 m_items
.Append( mitem
);
492 void wxMenu::Append( int id
, const wxString
&text
, wxMenu
*subMenu
, const wxString
&helpStr
)
494 wxMenuItem
*mitem
= new wxMenuItem();
496 mitem
->SetText(text
);
497 mitem
->SetHelp(helpStr
);
499 GtkWidget
*menuItem
= gtk_menu_item_new_with_label(mitem
->GetText());
500 mitem
->SetMenuItem(menuItem
);
501 mitem
->SetSubMenu(subMenu
);
503 gtk_signal_connect( GTK_OBJECT(menuItem
), "select",
504 GTK_SIGNAL_FUNC(gtk_menu_hilight_callback
),
507 gtk_signal_connect( GTK_OBJECT(menuItem
), "deselect",
508 GTK_SIGNAL_FUNC(gtk_menu_nolight_callback
),
511 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menuItem
), subMenu
->m_menu
);
512 gtk_menu_append( GTK_MENU(m_menu
), menuItem
);
513 gtk_widget_show( menuItem
);
514 m_items
.Append( mitem
);
517 void wxMenu::Append( wxMenuItem
*item
)
519 m_items
.Append( item
);
521 GtkWidget
*menuItem
= (GtkWidget
*) NULL
;
523 if (item
->IsSeparator())
524 menuItem
= gtk_menu_item_new();
525 else if (item
->IsSubMenu())
526 menuItem
= gtk_menu_item_new_with_label(item
->GetText());
528 menuItem
= item
->IsCheckable() ? gtk_check_menu_item_new_with_label(item
->GetText())
529 : gtk_menu_item_new_with_label(item
->GetText());
531 if (!item
->IsSeparator())
533 gtk_signal_connect( GTK_OBJECT(menuItem
), "select",
534 GTK_SIGNAL_FUNC(gtk_menu_hilight_callback
),
537 gtk_signal_connect( GTK_OBJECT(menuItem
), "deselect",
538 GTK_SIGNAL_FUNC(gtk_menu_nolight_callback
),
541 if (!item
->IsSubMenu())
543 gtk_signal_connect( GTK_OBJECT(menuItem
), "activate",
544 GTK_SIGNAL_FUNC(gtk_menu_clicked_callback
),
549 gtk_menu_append( GTK_MENU(m_menu
), menuItem
);
550 gtk_widget_show( menuItem
);
551 item
->SetMenuItem(menuItem
);
554 int wxMenu::FindItem( const wxString itemString
) const
556 wxString
s( itemString
);
561 pos
= s
.First( '&' );
562 if (pos
!= -1) s
.Remove( pos
, 1 );
565 wxNode
*node
= m_items
.First();
568 wxMenuItem
*item
= (wxMenuItem
*)node
->Data();
569 if (item
->GetText() == s
)
571 return item
->GetId();
579 void wxMenu::Enable( int id
, bool enable
)
581 wxMenuItem
*item
= FindItem(id
);
584 item
->Enable(enable
);
588 bool wxMenu::IsEnabled( int id
) const
590 wxMenuItem
*item
= FindItem(id
);
593 return item
->IsEnabled();
601 void wxMenu::Check( int id
, bool enable
)
603 wxMenuItem
*item
= FindItem(id
);
610 bool wxMenu::IsChecked( int id
) const
612 wxMenuItem
*item
= FindItem(id
);
615 return item
->IsChecked();
623 void wxMenu::SetLabel( int id
, const wxString
&label
)
625 wxMenuItem
*item
= FindItem(id
);
628 item
->SetText(label
);
632 wxString
wxMenu::GetLabel( int id
) const
634 wxMenuItem
*item
= FindItem(id
);
637 return item
->GetText();
645 void wxMenu::SetHelpString( int id
, const wxString
& helpString
)
647 wxMenuItem
*item
= FindItem(id
);
648 if (item
) item
->SetHelp( helpString
);
651 wxString
wxMenu::GetHelpString( int id
) const
653 wxMenuItem
*item
= FindItem(id
);
656 return item
->GetHelp();
664 int wxMenu::FindMenuIdByMenuItem( GtkWidget
*menuItem
) const
666 wxNode
*node
= m_items
.First();
669 wxMenuItem
*item
= (wxMenuItem
*)node
->Data();
670 if (item
->GetMenuItem() == menuItem
)
671 return item
->GetId();
678 wxMenuItem
*wxMenu::FindItem(int id
) const
680 wxNode
*node
= m_items
.First();
683 wxMenuItem
*item
= (wxMenuItem
*)node
->Data();
684 if (item
->GetId() == id
)
691 /* Not finding anything here can be correct
692 * when search the entire menu system for
693 * an entry -> no error message. */
695 return (wxMenuItem
*) NULL
;
698 void wxMenu::SetInvokingWindow( wxWindow
*win
)
700 m_invokingWindow
= win
;
703 wxWindow
*wxMenu::GetInvokingWindow()
705 return m_invokingWindow
;
708 // Update a menu and all submenus recursively.
709 // source is the object that has the update event handlers
710 // defined for it. If NULL, the menu or associated window
712 void wxMenu::UpdateUI(wxEvtHandler
* source
)
714 if (!source
&& GetInvokingWindow())
715 source
= GetInvokingWindow()->GetEventHandler();
717 source
= GetEventHandler();
721 wxNode
* node
= GetItems().First();
724 wxMenuItem
* item
= (wxMenuItem
*) node
->Data();
725 if ( !item
->IsSeparator() )
727 wxWindowID id
= item
->GetId();
728 wxUpdateUIEvent
event(id
);
729 event
.SetEventObject( source
);
731 if (source
->ProcessEvent(event
))
733 if (event
.GetSetText())
734 SetLabel(id
, event
.GetText());
735 if (event
.GetSetChecked())
736 Check(id
, event
.GetChecked());
737 if (event
.GetSetEnabled())
738 Enable(id
, event
.GetEnabled());
741 if (item
->GetSubMenu())
742 item
->GetSubMenu()->UpdateUI(source
);