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 char *pc
= title
; *pc
!= '\0'; pc
++ )
82 #if (GTK_MINOR_VERSION > 0)
90 menu
->m_owner
= gtk_menu_item_new_with_label( WXSTRINGCAST(s
) );
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
, "wxMenuBar::Check: no such item" );
186 bool wxMenuBar::IsChecked( int id
) const
188 wxMenuItem
* item
= FindMenuItemById( id
);
190 wxCHECK_MSG( item
, FALSE
, "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
, "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
, "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
, "", "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
, "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
, "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
, "invalid", "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
, "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
, "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
, "", "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
, "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 char *pc
= str
; *pc
!= '\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
.c_str());
427 void wxMenuItem::Check( bool check
)
429 wxCHECK_RET( m_menuItem
, "invalid menu item" );
431 wxCHECK_RET( IsCheckable(), "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
, "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
, "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
= "";
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 char *text
= mitem
->GetText();
530 #if (GTK_MINOR_VERSION > 0)
535 GtkItemFactoryEntry entry
;
537 entry
.accelerator
= (gchar
*) NULL
;
538 entry
.callback
= (GtkItemFactoryCallback
) gtk_menu_clicked_callback
;
539 entry
.callback_action
= 0;
541 entry
.item_type
= "<CheckItem>";
543 entry
.item_type
= "<Item>";
545 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); /* what is 2 ? */
547 /* in order to get the pointer to the item we need the item text _without_ underscores */
548 wxString s
= "<main>/";
549 for ( const char *pc
= text
; *pc
!= '\0'; pc
++ )
551 if (*pc
== '_') pc
++; /* skip it */
555 GtkWidget
*menuItem
= gtk_item_factory_get_widget( m_factory
, s
);
559 GtkWidget
*menuItem
= checkable
? gtk_check_menu_item_new_with_label(text
)
560 : gtk_menu_item_new_with_label(text
);
562 gtk_signal_connect( GTK_OBJECT(menuItem
), "activate",
563 GTK_SIGNAL_FUNC(gtk_menu_clicked_callback
),
566 gtk_menu_append( GTK_MENU(m_menu
), menuItem
);
567 gtk_widget_show( menuItem
);
571 gtk_signal_connect( GTK_OBJECT(menuItem
), "select",
572 GTK_SIGNAL_FUNC(gtk_menu_hilight_callback
),
575 gtk_signal_connect( GTK_OBJECT(menuItem
), "deselect",
576 GTK_SIGNAL_FUNC(gtk_menu_nolight_callback
),
579 mitem
->SetMenuItem(menuItem
);
581 m_items
.Append( mitem
);
584 void wxMenu::Append( int id
, const wxString
&text
, wxMenu
*subMenu
, const wxString
&helpStr
)
586 wxMenuItem
*mitem
= new wxMenuItem();
588 mitem
->SetText(text
);
589 mitem
->SetHelp(helpStr
);
591 GtkWidget
*menuItem
= gtk_menu_item_new_with_label(mitem
->GetText());
592 mitem
->SetMenuItem(menuItem
);
593 mitem
->SetSubMenu(subMenu
);
595 gtk_signal_connect( GTK_OBJECT(menuItem
), "select",
596 GTK_SIGNAL_FUNC(gtk_menu_hilight_callback
),
599 gtk_signal_connect( GTK_OBJECT(menuItem
), "deselect",
600 GTK_SIGNAL_FUNC(gtk_menu_nolight_callback
),
603 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menuItem
), subMenu
->m_menu
);
604 gtk_menu_append( GTK_MENU(m_menu
), menuItem
);
605 gtk_widget_show( menuItem
);
606 m_items
.Append( mitem
);
609 void wxMenu::Append( wxMenuItem
*item
)
611 m_items
.Append( item
);
613 GtkWidget
*menuItem
= (GtkWidget
*) NULL
;
615 if (item
->IsSeparator())
616 menuItem
= gtk_menu_item_new();
617 else if (item
->IsSubMenu())
618 menuItem
= gtk_menu_item_new_with_label(item
->GetText());
620 menuItem
= item
->IsCheckable() ? gtk_check_menu_item_new_with_label(item
->GetText())
621 : gtk_menu_item_new_with_label(item
->GetText());
623 if (!item
->IsSeparator())
625 gtk_signal_connect( GTK_OBJECT(menuItem
), "select",
626 GTK_SIGNAL_FUNC(gtk_menu_hilight_callback
),
629 gtk_signal_connect( GTK_OBJECT(menuItem
), "deselect",
630 GTK_SIGNAL_FUNC(gtk_menu_nolight_callback
),
633 if (!item
->IsSubMenu())
635 gtk_signal_connect( GTK_OBJECT(menuItem
), "activate",
636 GTK_SIGNAL_FUNC(gtk_menu_clicked_callback
),
641 gtk_menu_append( GTK_MENU(m_menu
), menuItem
);
642 gtk_widget_show( menuItem
);
643 item
->SetMenuItem(menuItem
);
646 int wxMenu::FindItem( const wxString itemString
) const
649 for ( const char *pc
= itemString
; *pc
!= '\0'; pc
++ )
654 #if (GTK_MINOR_VERSION > 0)
661 wxNode
*node
= m_items
.First();
664 wxMenuItem
*item
= (wxMenuItem
*)node
->Data();
665 if (item
->GetText() == s
)
667 return item
->GetId();
675 void wxMenu::Enable( int id
, bool enable
)
677 wxMenuItem
*item
= FindItem(id
);
679 wxCHECK_RET( item
, "wxMenu::Enable: no such item" );
681 item
->Enable(enable
);
684 bool wxMenu::IsEnabled( int id
) const
686 wxMenuItem
*item
= FindItem(id
);
688 wxCHECK_MSG( item
, FALSE
, "wxMenu::IsEnabled: no such item" );
690 return item
->IsEnabled();
693 void wxMenu::Check( int id
, bool enable
)
695 wxMenuItem
*item
= FindItem(id
);
697 wxCHECK_RET( item
, "wxMenu::Check: no such item" );
702 bool wxMenu::IsChecked( int id
) const
704 wxMenuItem
*item
= FindItem(id
);
706 wxCHECK_MSG( item
, FALSE
, "wxMenu::IsChecked: no such item" );
708 return item
->IsChecked();
711 void wxMenu::SetLabel( int id
, const wxString
&label
)
713 wxMenuItem
*item
= FindItem(id
);
715 wxCHECK_RET( item
, "wxMenu::SetLabel: no such item" );
717 item
->SetText(label
);
720 wxString
wxMenu::GetLabel( int id
) const
722 wxMenuItem
*item
= FindItem(id
);
724 wxCHECK_MSG( item
, "", "wxMenu::GetLabel: no such item" );
726 return item
->GetText();
729 void wxMenu::SetHelpString( int id
, const wxString
& helpString
)
731 wxMenuItem
*item
= FindItem(id
);
733 wxCHECK_RET( item
, "wxMenu::SetHelpString: no such item" );
735 item
->SetHelp( helpString
);
738 wxString
wxMenu::GetHelpString( int id
) const
740 wxMenuItem
*item
= FindItem(id
);
742 wxCHECK_MSG( item
, "", "wxMenu::GetHelpString: no such item" );
744 return item
->GetHelp();
747 int wxMenu::FindMenuIdByMenuItem( GtkWidget
*menuItem
) const
749 wxNode
*node
= m_items
.First();
752 wxMenuItem
*item
= (wxMenuItem
*)node
->Data();
753 if (item
->GetMenuItem() == menuItem
)
754 return item
->GetId();
761 wxMenuItem
*wxMenu::FindItem(int id
) const
763 wxNode
*node
= m_items
.First();
766 wxMenuItem
*item
= (wxMenuItem
*)node
->Data();
767 if (item
->GetId() == id
)
774 /* Not finding anything here can be correct
775 * when search the entire menu system for
776 * an entry -> no error message. */
778 return (wxMenuItem
*) NULL
;
781 void wxMenu::SetInvokingWindow( wxWindow
*win
)
783 m_invokingWindow
= win
;
786 wxWindow
*wxMenu::GetInvokingWindow()
788 return m_invokingWindow
;
791 // Update a menu and all submenus recursively. source is the object that has
792 // the update event handlers defined for it. If NULL, the menu or associated
793 // window will be used.
794 void wxMenu::UpdateUI(wxEvtHandler
* source
)
796 if (!source
&& GetInvokingWindow())
797 source
= GetInvokingWindow()->GetEventHandler();
799 source
= GetEventHandler();
803 wxNode
* node
= GetItems().First();
806 wxMenuItem
* item
= (wxMenuItem
*) node
->Data();
807 if ( !item
->IsSeparator() )
809 wxWindowID id
= item
->GetId();
810 wxUpdateUIEvent
event(id
);
811 event
.SetEventObject( source
);
813 if (source
->ProcessEvent(event
))
815 if (event
.GetSetText())
816 SetLabel(id
, event
.GetText());
817 if (event
.GetSetChecked())
818 Check(id
, event
.GetChecked());
819 if (event
.GetSetEnabled())
820 Enable(id
, event
.GetEnabled());
823 if (item
->GetSubMenu())
824 item
->GetSubMenu()->UpdateUI(source
);