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
, style
, "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
);
77 for ( const wxChar
*pc
= title
; *pc
!= _T('\0'); pc
++ )
82 #if (GTK_MINOR_VERSION > 0)
90 menu
->m_owner
= gtk_menu_item_new_with_label( MBSTRINGCAST s
.mbc_str() );
91 gtk_widget_show( menu
->m_owner
);
92 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu
->m_owner
), menu
->m_menu
);
94 gtk_menu_bar_append( GTK_MENU_BAR(m_menubar
), menu
->m_owner
);
97 static int FindMenuItemRecursive( const wxMenu
*menu
, const wxString
&menuString
, const wxString
&itemString
)
99 if (menu
->GetTitle() == menuString
)
101 int res
= menu
->FindItem( itemString
);
102 if (res
!= wxNOT_FOUND
)
106 wxNode
*node
= ((wxMenu
*)menu
)->GetItems().First(); // const_cast
109 wxMenuItem
*item
= (wxMenuItem
*)node
->Data();
110 if (item
->IsSubMenu())
111 return FindMenuItemRecursive(item
->GetSubMenu(), menuString
, itemString
);
119 wxMenuItem
*wxMenuBar::FindItemForId(int itemId
, wxMenu
**menuForItem
= NULL
) const
123 // TODO return the pointer to the menu
128 return FindItem(itemId
);
131 int wxMenuBar::FindMenuItem( const wxString
&menuString
, const wxString
&itemString
) const
133 wxNode
*node
= m_menus
.First();
136 wxMenu
*menu
= (wxMenu
*)node
->Data();
137 int res
= FindMenuItemRecursive( menu
, menuString
, itemString
);
138 if (res
!= -1) return res
;
144 // Find a wxMenuItem using its id. Recurses down into sub-menus
145 static wxMenuItem
* FindMenuItemByIdRecursive(const wxMenu
* menu
, int id
)
147 wxMenuItem
* result
= menu
->FindItem(id
);
149 wxNode
*node
= ((wxMenu
*)menu
)->GetItems().First(); // const_cast
150 while ( node
&& result
== NULL
)
152 wxMenuItem
*item
= (wxMenuItem
*)node
->Data();
153 if (item
->IsSubMenu())
155 result
= FindMenuItemByIdRecursive( item
->GetSubMenu(), id
);
163 wxMenuItem
* wxMenuBar::FindItem( int id
) const
165 wxMenuItem
* result
= 0;
166 wxNode
*node
= m_menus
.First();
167 while (node
&& result
== 0)
169 wxMenu
*menu
= (wxMenu
*)node
->Data();
170 result
= FindMenuItemByIdRecursive( menu
, id
);
177 void wxMenuBar::Check( int id
, bool check
)
179 wxMenuItem
* item
= FindMenuItemById( id
);
181 wxCHECK_RET( item
, _T("wxMenuBar::Check: no such item") );
186 bool wxMenuBar::IsChecked( int id
) const
188 wxMenuItem
* item
= FindMenuItemById( id
);
190 wxCHECK_MSG( item
, FALSE
, _T("wxMenuBar::IsChecked: no such item") );
192 return item
->IsChecked();
195 void wxMenuBar::Enable( int id
, bool enable
)
197 wxMenuItem
* item
= FindMenuItemById( id
);
199 wxCHECK_RET( item
, _T("wxMenuBar::Enable: no such item") );
201 item
->Enable(enable
);
204 bool wxMenuBar::IsEnabled( int id
) const
206 wxMenuItem
* item
= FindMenuItemById( id
);
208 wxCHECK_MSG( item
, FALSE
, _T("wxMenuBar::IsEnabled: no such item") );
210 return item
->IsEnabled();
213 wxString
wxMenuBar::GetLabel( int id
) const
215 wxMenuItem
* item
= FindMenuItemById( id
);
217 wxCHECK_MSG( item
, _T(""), _T("wxMenuBar::GetLabel: no such item") );
219 return item
->GetText();
222 void wxMenuBar::SetLabel( int id
, const wxString
&label
)
224 wxMenuItem
* item
= FindMenuItemById( id
);
226 wxCHECK_RET( item
, _T("wxMenuBar::SetLabel: no such item") );
228 item
->SetText( label
);
231 void wxMenuBar::EnableTop( int pos
, bool flag
)
233 wxNode
*node
= m_menus
.Nth( pos
);
235 wxCHECK_RET( node
, _T("menu not found") );
237 wxMenu
* menu
= (wxMenu
*)node
->Data();
240 gtk_widget_set_sensitive( menu
->m_owner
, flag
);
243 wxString
wxMenuBar::GetLabelTop( int pos
) const
245 wxNode
*node
= m_menus
.Nth( pos
);
247 wxCHECK_MSG( node
, _T("invalid"), _T("menu not found") );
249 wxMenu
* menu
= (wxMenu
*)node
->Data();
251 return menu
->GetTitle();
254 void wxMenuBar::SetLabelTop( int pos
, const wxString
& label
)
256 wxNode
*node
= m_menus
.Nth( pos
);
258 wxCHECK_RET( node
, _T("menu not found") );
260 wxMenu
* menu
= (wxMenu
*)node
->Data();
262 menu
->SetTitle( label
);
265 void wxMenuBar::SetHelpString( int id
, const wxString
& helpString
)
267 wxMenuItem
* item
= FindMenuItemById( id
);
269 wxCHECK_RET( item
, _T("wxMenuBar::SetHelpString: no such item") );
271 item
->SetHelp( helpString
);
274 wxString
wxMenuBar::GetHelpString( int id
) const
276 wxMenuItem
* item
= FindMenuItemById( id
);
278 wxCHECK_MSG( item
, _T(""), _T("wxMenuBar::GetHelpString: no such item") );
280 return item
->GetHelp();
283 //-----------------------------------------------------------------------------
285 //-----------------------------------------------------------------------------
287 static void gtk_menu_clicked_callback( GtkWidget
*widget
, wxMenu
*menu
)
289 int id
= menu
->FindMenuIdByMenuItem(widget
);
291 /* should find it for normal (not popup) menu */
292 wxASSERT( (id
!= -1) || (menu
->GetInvokingWindow() != NULL
) );
294 if (!menu
->IsEnabled(id
))
297 wxMenuItem
* item
= menu
->FindItem( id
);
298 wxCHECK_RET( item
, _T("error in menu item callback") );
300 if (item
->IsCheckable())
302 if (item
->GetCheckedFlag() == item
->IsChecked())
304 /* the menu item has been checked by calling wxMenuItem->Check() */
309 /* the user pressed on the menu item -> report */
310 item
->SetCheckedFlag(item
->IsChecked()); /* make consistent again */
314 wxCommandEvent
event( wxEVT_COMMAND_MENU_SELECTED
, id
);
315 event
.SetEventObject( menu
);
318 if (menu
->GetCallback())
320 (void) (*(menu
->GetCallback())) (*menu
, event
);
324 if (menu
->GetEventHandler()->ProcessEvent(event
))
327 wxWindow
*win
= menu
->GetInvokingWindow();
329 win
->GetEventHandler()->ProcessEvent( event
);
332 //-----------------------------------------------------------------------------
334 //-----------------------------------------------------------------------------
336 static void gtk_menu_hilight_callback( GtkWidget
*widget
, wxMenu
*menu
)
338 int id
= menu
->FindMenuIdByMenuItem(widget
);
340 wxASSERT( id
!= -1 ); // should find it!
342 if (!menu
->IsEnabled(id
))
345 wxMenuEvent
event( wxEVT_MENU_HIGHLIGHT
, id
);
346 event
.SetEventObject( menu
);
348 /* wxMSW doesn't call callback here either
350 if (menu->m_callback)
352 (void) (*(menu->m_callback)) (*menu, event);
357 if (menu
->GetEventHandler()->ProcessEvent(event
))
360 wxWindow
*win
= menu
->GetInvokingWindow();
361 if (win
) win
->GetEventHandler()->ProcessEvent( event
);
364 //-----------------------------------------------------------------------------
366 //-----------------------------------------------------------------------------
368 static void gtk_menu_nolight_callback( GtkWidget
*widget
, wxMenu
*menu
)
370 int id
= menu
->FindMenuIdByMenuItem(widget
);
372 wxASSERT( id
!= -1 ); // should find it!
374 if (!menu
->IsEnabled(id
))
377 wxMenuEvent
event( wxEVT_MENU_HIGHLIGHT
, -1 );
378 event
.SetEventObject( menu
);
380 if (menu
->GetEventHandler()->ProcessEvent(event
))
383 wxWindow
*win
= menu
->GetInvokingWindow();
385 win
->GetEventHandler()->ProcessEvent( event
);
388 //-----------------------------------------------------------------------------
390 //-----------------------------------------------------------------------------
392 IMPLEMENT_DYNAMIC_CLASS(wxMenuItem
,wxObject
)
394 wxMenuItem::wxMenuItem()
397 m_isCheckMenu
= FALSE
;
400 m_subMenu
= (wxMenu
*) NULL
;
401 m_menuItem
= (GtkWidget
*) NULL
;
404 // it's valid for this function to be called even if m_menuItem == NULL
405 void wxMenuItem::SetName( const wxString
& str
)
408 for ( const wxChar
*pc
= str
; *pc
!= _T('\0'); pc
++ )
413 #if (GTK_MINOR_VERSION > 0)
422 GtkLabel
*label
= GTK_LABEL( GTK_BIN(m_menuItem
)->child
);
423 gtk_label_set( label
, m_text
.mbc_str());
427 void wxMenuItem::Check( bool check
)
429 wxCHECK_RET( m_menuItem
, _T("invalid menu item") );
431 wxCHECK_RET( IsCheckable(), _T("Can't check uncheckable item!") )
433 if (check
== m_isChecked
) return;
436 gtk_check_menu_item_set_state( (GtkCheckMenuItem
*)m_menuItem
, (gint
)check
);
439 void wxMenuItem::Enable( bool enable
)
441 wxCHECK_RET( m_menuItem
, _T("invalid menu item") );
443 gtk_widget_set_sensitive( m_menuItem
, enable
);
444 m_isEnabled
= enable
;
447 bool wxMenuItem::IsChecked() const
449 wxCHECK_MSG( m_menuItem
, FALSE
, _T("invalid menu item") );
451 wxCHECK( IsCheckable(), FALSE
); // can't get state of uncheckable item!
453 bool bIsChecked
= ((GtkCheckMenuItem
*)m_menuItem
)->active
!= 0;
458 //-----------------------------------------------------------------------------
460 //-----------------------------------------------------------------------------
462 IMPLEMENT_DYNAMIC_CLASS(wxMenu
,wxEvtHandler
)
464 wxMenu::wxMenu( const wxString
& title
, const wxFunction func
)
467 m_items
.DeleteContents( TRUE
);
468 m_invokingWindow
= (wxWindow
*) NULL
;
470 #if (GTK_MINOR_VERSION > 0)
471 m_accel
= gtk_accel_group_new();
472 m_factory
= gtk_item_factory_new( GTK_TYPE_MENU
, "<main>", m_accel
);
473 m_menu
= gtk_item_factory_get_widget( m_factory
, "<main>" );
475 m_menu
= gtk_menu_new(); // Do not show!
479 m_eventHandler
= this;
480 m_clientData
= (void*) NULL
;
482 if (m_title
.IsNull()) m_title
= _T("");
483 if (m_title
!= _T(""))
489 m_owner
= (GtkWidget
*) NULL
;
494 /* how do we delete an item-factory ? */
497 void wxMenu::SetTitle( const wxString
& title
)
499 // TODO Waiting for something better
503 const wxString
wxMenu::GetTitle() const
508 void wxMenu::AppendSeparator()
510 wxMenuItem
*mitem
= new wxMenuItem();
511 mitem
->SetId(ID_SEPARATOR
);
513 GtkWidget
*menuItem
= gtk_menu_item_new();
514 gtk_menu_append( GTK_MENU(m_menu
), menuItem
);
515 gtk_widget_show( menuItem
);
517 mitem
->SetMenuItem(menuItem
);
518 m_items
.Append( mitem
);
521 void wxMenu::Append( int id
, const wxString
&item
, const wxString
&helpStr
, bool checkable
)
523 wxMenuItem
*mitem
= new wxMenuItem();
525 mitem
->SetText(item
);
526 mitem
->SetHelp(helpStr
);
527 mitem
->SetCheckable(checkable
);
528 const wxChar
*text
= mitem
->GetText();
530 #if (GTK_MINOR_VERSION > 0)
532 wxStrcpy( buf
, _T("/") );
533 wxStrcat( buf
, text
);
535 const wxWX2MBbuf pbuf
= wxConv_current
->cWX2MB(buf
);
536 GtkItemFactoryEntry entry
;
537 entry
.path
= MBSTRINGCAST pbuf
;
538 entry
.accelerator
= (gchar
*) NULL
;
539 entry
.callback
= (GtkItemFactoryCallback
) gtk_menu_clicked_callback
;
540 entry
.callback_action
= 0;
542 entry
.item_type
= "<CheckItem>";
544 entry
.item_type
= "<Item>";
546 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); /* what is 2 ? */
548 /* in order to get the pointer to the item we need the item text _without_ underscores */
549 wxString s
= _T("<main>/");
550 for ( const wxChar
*pc
= text
; *pc
!= _T('\0'); pc
++ )
552 if (*pc
== _T('_')) pc
++; /* skip it */
556 GtkWidget
*menuItem
= gtk_item_factory_get_widget( m_factory
, s
.mbc_str() );
560 GtkWidget
*menuItem
= checkable
? gtk_check_menu_item_new_with_label(text
)
561 : gtk_menu_item_new_with_label(text
);
563 gtk_signal_connect( GTK_OBJECT(menuItem
), "activate",
564 GTK_SIGNAL_FUNC(gtk_menu_clicked_callback
),
567 gtk_menu_append( GTK_MENU(m_menu
), menuItem
);
568 gtk_widget_show( menuItem
);
572 gtk_signal_connect( GTK_OBJECT(menuItem
), "select",
573 GTK_SIGNAL_FUNC(gtk_menu_hilight_callback
),
576 gtk_signal_connect( GTK_OBJECT(menuItem
), "deselect",
577 GTK_SIGNAL_FUNC(gtk_menu_nolight_callback
),
580 mitem
->SetMenuItem(menuItem
);
582 m_items
.Append( mitem
);
585 void wxMenu::Append( int id
, const wxString
&text
, wxMenu
*subMenu
, const wxString
&helpStr
)
587 wxMenuItem
*mitem
= new wxMenuItem();
589 mitem
->SetText(text
);
590 mitem
->SetHelp(helpStr
);
592 GtkWidget
*menuItem
= gtk_menu_item_new_with_label(mitem
->GetText().mbc_str());
593 mitem
->SetMenuItem(menuItem
);
594 mitem
->SetSubMenu(subMenu
);
596 gtk_signal_connect( GTK_OBJECT(menuItem
), "select",
597 GTK_SIGNAL_FUNC(gtk_menu_hilight_callback
),
600 gtk_signal_connect( GTK_OBJECT(menuItem
), "deselect",
601 GTK_SIGNAL_FUNC(gtk_menu_nolight_callback
),
604 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menuItem
), subMenu
->m_menu
);
605 gtk_menu_append( GTK_MENU(m_menu
), menuItem
);
606 gtk_widget_show( menuItem
);
607 m_items
.Append( mitem
);
610 void wxMenu::Append( wxMenuItem
*item
)
612 m_items
.Append( item
);
614 GtkWidget
*menuItem
= (GtkWidget
*) NULL
;
616 if (item
->IsSeparator())
617 menuItem
= gtk_menu_item_new();
618 else if (item
->IsSubMenu())
619 menuItem
= gtk_menu_item_new_with_label(item
->GetText().mbc_str());
621 menuItem
= item
->IsCheckable() ? gtk_check_menu_item_new_with_label(item
->GetText().mbc_str())
622 : gtk_menu_item_new_with_label(item
->GetText().mbc_str());
624 if (!item
->IsSeparator())
626 gtk_signal_connect( GTK_OBJECT(menuItem
), "select",
627 GTK_SIGNAL_FUNC(gtk_menu_hilight_callback
),
630 gtk_signal_connect( GTK_OBJECT(menuItem
), "deselect",
631 GTK_SIGNAL_FUNC(gtk_menu_nolight_callback
),
634 if (!item
->IsSubMenu())
636 gtk_signal_connect( GTK_OBJECT(menuItem
), "activate",
637 GTK_SIGNAL_FUNC(gtk_menu_clicked_callback
),
642 gtk_menu_append( GTK_MENU(m_menu
), menuItem
);
643 gtk_widget_show( menuItem
);
644 item
->SetMenuItem(menuItem
);
647 int wxMenu::FindItem( const wxString itemString
) const
650 for ( const wxChar
*pc
= itemString
; *pc
!= _T('\0'); pc
++ )
655 #if (GTK_MINOR_VERSION > 0)
662 wxNode
*node
= m_items
.First();
665 wxMenuItem
*item
= (wxMenuItem
*)node
->Data();
666 if (item
->GetText() == s
)
668 return item
->GetId();
676 void wxMenu::Enable( int id
, bool enable
)
678 wxMenuItem
*item
= FindItem(id
);
680 wxCHECK_RET( item
, _T("wxMenu::Enable: no such item") );
682 item
->Enable(enable
);
685 bool wxMenu::IsEnabled( int id
) const
687 wxMenuItem
*item
= FindItem(id
);
689 wxCHECK_MSG( item
, FALSE
, _T("wxMenu::IsEnabled: no such item") );
691 return item
->IsEnabled();
694 void wxMenu::Check( int id
, bool enable
)
696 wxMenuItem
*item
= FindItem(id
);
698 wxCHECK_RET( item
, _T("wxMenu::Check: no such item") );
703 bool wxMenu::IsChecked( int id
) const
705 wxMenuItem
*item
= FindItem(id
);
707 wxCHECK_MSG( item
, FALSE
, _T("wxMenu::IsChecked: no such item") );
709 return item
->IsChecked();
712 void wxMenu::SetLabel( int id
, const wxString
&label
)
714 wxMenuItem
*item
= FindItem(id
);
716 wxCHECK_RET( item
, _T("wxMenu::SetLabel: no such item") );
718 item
->SetText(label
);
721 wxString
wxMenu::GetLabel( int id
) const
723 wxMenuItem
*item
= FindItem(id
);
725 wxCHECK_MSG( item
, _T(""), _T("wxMenu::GetLabel: no such item") );
727 return item
->GetText();
730 void wxMenu::SetHelpString( int id
, const wxString
& helpString
)
732 wxMenuItem
*item
= FindItem(id
);
734 wxCHECK_RET( item
, _T("wxMenu::SetHelpString: no such item") );
736 item
->SetHelp( helpString
);
739 wxString
wxMenu::GetHelpString( int id
) const
741 wxMenuItem
*item
= FindItem(id
);
743 wxCHECK_MSG( item
, _T(""), _T("wxMenu::GetHelpString: no such item") );
745 return item
->GetHelp();
748 int wxMenu::FindMenuIdByMenuItem( GtkWidget
*menuItem
) const
750 wxNode
*node
= m_items
.First();
753 wxMenuItem
*item
= (wxMenuItem
*)node
->Data();
754 if (item
->GetMenuItem() == menuItem
)
755 return item
->GetId();
762 wxMenuItem
*wxMenu::FindItem(int id
) const
764 wxNode
*node
= m_items
.First();
767 wxMenuItem
*item
= (wxMenuItem
*)node
->Data();
768 if (item
->GetId() == id
)
775 /* Not finding anything here can be correct
776 * when search the entire menu system for
777 * an entry -> no error message. */
779 return (wxMenuItem
*) NULL
;
782 void wxMenu::SetInvokingWindow( wxWindow
*win
)
784 m_invokingWindow
= win
;
787 wxWindow
*wxMenu::GetInvokingWindow()
789 return m_invokingWindow
;
792 // Update a menu and all submenus recursively. source is the object that has
793 // the update event handlers defined for it. If NULL, the menu or associated
794 // window will be used.
795 void wxMenu::UpdateUI(wxEvtHandler
* source
)
797 if (!source
&& GetInvokingWindow())
798 source
= GetInvokingWindow()->GetEventHandler();
800 source
= GetEventHandler();
804 wxNode
* node
= GetItems().First();
807 wxMenuItem
* item
= (wxMenuItem
*) node
->Data();
808 if ( !item
->IsSeparator() )
810 wxWindowID id
= item
->GetId();
811 wxUpdateUIEvent
event(id
);
812 event
.SetEventObject( source
);
814 if (source
->ProcessEvent(event
))
816 if (event
.GetSetText())
817 SetLabel(id
, event
.GetText());
818 if (event
.GetSetChecked())
819 Check(id
, event
.GetChecked());
820 if (event
.GetSetEnabled())
821 Enable(id
, event
.GetEnabled());
824 if (item
->GetSubMenu())
825 item
->GetSubMenu()->UpdateUI(source
);