The rounded corners look really dumb at this size.
[wxWidgets.git] / src / osx / menu_osx.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/osx/menu_osx.cpp
3 // Purpose: wxMenu, wxMenuBar, wxMenuItem
4 // Author: Stefan Csomor
5 // Modified by:
6 // Created: 1998-01-01
7 // Copyright: (c) Stefan Csomor
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // ============================================================================
12 // headers & declarations
13 // ============================================================================
14
15 // wxWidgets headers
16 // -----------------
17
18 #include "wx/wxprec.h"
19
20 #if wxUSE_MENUS
21
22 #include "wx/menu.h"
23
24 #ifndef WX_PRECOMP
25 #include "wx/log.h"
26 #include "wx/app.h"
27 #include "wx/utils.h"
28 #include "wx/frame.h"
29 #include "wx/dialog.h"
30 #include "wx/menuitem.h"
31 #endif
32
33 #include "wx/osx/private.h"
34
35 // other standard headers
36 // ----------------------
37 #include <string.h>
38
39 IMPLEMENT_ABSTRACT_CLASS( wxMenuImpl , wxObject )
40
41 wxMenuImpl::~wxMenuImpl()
42 {
43 }
44
45 // the (popup) menu title has this special menuid
46 static const int idMenuTitle = -3;
47
48 // ============================================================================
49 // implementation
50 // ============================================================================
51
52 // Menus
53
54 // Construct a menu with optional title (then use append)
55
56 void wxMenu::Init()
57 {
58 m_doBreak = false;
59 m_allowRearrange = true;
60 m_noEventsMode = false;
61
62 m_peer = wxMenuImpl::Create( this, wxStripMenuCodes(m_title) );
63
64
65 // if we have a title, insert it in the beginning of the menu
66 if ( !m_title.empty() )
67 {
68 Append(idMenuTitle, m_title) ;
69 AppendSeparator() ;
70 }
71 }
72
73 wxMenu::~wxMenu()
74 {
75 delete m_peer;
76 }
77
78 WXHMENU wxMenu::GetHMenu() const
79 {
80 if ( m_peer )
81 return m_peer->GetHMenu();
82 return NULL;
83 }
84
85 void wxMenu::Break()
86 {
87 // not available on the mac platform
88 }
89
90 void wxMenu::SetAllowRearrange( bool allow )
91 {
92 m_allowRearrange = allow;
93 }
94
95 void wxMenu::SetNoEventsMode( bool noEvents )
96 {
97 m_noEventsMode = noEvents;
98 }
99
100 // function appends a new item or submenu to the menu
101 // append a new item or submenu to the menu
102 bool wxMenu::DoInsertOrAppend(wxMenuItem *item, size_t pos)
103 {
104 wxASSERT_MSG( item != NULL, wxT("can't append NULL item to the menu") );
105 GetPeer()->InsertOrAppend( item, pos );
106
107 bool check = false;
108
109 if ( item->IsSeparator() )
110 {
111 // nothing to do here
112 }
113 else
114 {
115 wxMenu *pSubMenu = item->GetSubMenu() ;
116 if ( pSubMenu != NULL )
117 {
118 wxASSERT_MSG( pSubMenu->GetHMenu() != NULL , wxT("invalid submenu added"));
119 pSubMenu->m_menuParent = this ;
120
121 pSubMenu->DoRearrange();
122 }
123 else if ( item->IsRadio() )
124 {
125 // If a previous or next item is a radio button, add this radio
126 // button to the existing radio group. Otherwise start a new one
127 // for it.
128 wxMenuItemList& items = GetMenuItems();
129
130 size_t const
131 posItem = pos == (size_t)-1 ? items.GetCount() - 1 : pos;
132
133 wxMenuItemList::compatibility_iterator node = items.Item(posItem);
134 wxCHECK_MSG( node, false, wxS("New item must have been inserted") );
135
136 bool foundGroup = false;
137 if ( node->GetPrevious() )
138 {
139 wxMenuItem* const prev = node->GetPrevious()->GetData();
140
141 if ( prev->IsRadio() )
142 {
143 // This item is in the same group as the preceding one so
144 // we should use the same starting item, but getting it is
145 // a bit difficult as we can't query the start radio group
146 // item for it.
147 const int groupStart = prev->IsRadioGroupStart()
148 ? posItem - 1
149 : prev->GetRadioGroupStart();
150 item->SetRadioGroupStart(groupStart);
151
152 // We must also account for the new item by incrementing
153 // the index of the last item in this group.
154 wxMenuItem* const first = items.Item(groupStart)->GetData();
155 first->SetRadioGroupEnd(first->GetRadioGroupEnd() + 1);
156
157 foundGroup = true;
158 }
159 }
160
161 if ( !foundGroup && node->GetNext() )
162 {
163 wxMenuItem* const next = node->GetNext()->GetData();
164
165 if ( next->IsRadio() )
166 {
167 // This item is the new starting item of this group as the
168 // previous item is not a radio item.
169 wxASSERT_MSG( next->IsRadioGroupStart(),
170 wxS("Where is the start of this group?") );
171
172 // The index of the last item of the radio group must be
173 // incremented to account for the new item.
174 item->SetAsRadioGroupStart();
175 item->SetRadioGroupEnd(next->GetRadioGroupEnd() + 1);
176
177 // And the previous start item is not one any longer.
178 next->SetAsRadioGroupStart(false);
179
180 foundGroup = true;
181 }
182 }
183
184 if ( !foundGroup )
185 {
186 // start a new radio group
187 item->SetAsRadioGroupStart();
188 item->SetRadioGroupEnd(posItem);
189
190 // ensure that we have a checked item in the radio group
191 check = true;
192 }
193 }
194 else
195 {
196 if ( item->GetId() == idMenuTitle )
197 item->GetMenu()->Enable( idMenuTitle, false );
198 }
199 }
200
201 // We also need to update the indices of radio group start and end we store
202 // in any existing radio items after this item.
203 if ( pos < GetMenuItemCount() - 1 ) // takes into account pos == -1 case
204 {
205 for ( wxMenuItemList::compatibility_iterator
206 node = GetMenuItems().Item(pos + 1);
207 node;
208 node = node->GetNext() )
209 {
210 wxMenuItem* const item = node->GetData();
211 if ( item->IsRadio() )
212 {
213 if ( item->IsRadioGroupStart() )
214 {
215 // If the starting item is after the just inserted one,
216 // then the end one must be after it too and needs to be
217 // updated.
218 item->SetRadioGroupEnd(item->GetRadioGroupEnd() + 1);
219 }
220 else // Not the first radio group item.
221 {
222 // We need to update the start item index only if it is
223 // after the just inserted item.
224 const int groupStart = item->GetRadioGroupStart();
225 if ( (size_t)groupStart > pos )
226 item->SetRadioGroupStart(groupStart + 1);
227 }
228 }
229 }
230 }
231
232 // if we're already attached to the menubar, we must update it
233 if ( IsAttached() && GetMenuBar()->IsAttached() )
234 GetMenuBar()->Refresh();
235
236 if ( check )
237 item->Check(true);
238
239 return true ;
240 }
241
242 wxMenuItem* wxMenu::DoAppend(wxMenuItem *item)
243 {
244 if (wxMenuBase::DoAppend(item) && DoInsertOrAppend(item) )
245 return item;
246
247 return NULL;
248 }
249
250 wxMenuItem* wxMenu::DoInsert(size_t pos, wxMenuItem *item)
251 {
252 if (wxMenuBase::DoInsert(pos, item) && DoInsertOrAppend(item, pos))
253 return item;
254
255 return NULL;
256 }
257
258 wxMenuItem *wxMenu::DoRemove(wxMenuItem *item)
259 {
260 if ( item->IsRadio() )
261 {
262 // Check if we're removing the item starting the radio group
263 if ( item->IsRadioGroupStart() )
264 {
265 // Yes, we do, update the next radio group item, if any, to be the
266 // start one now.
267 const int endGroup = item->GetRadioGroupEnd();
268
269 wxMenuItemList::compatibility_iterator
270 node = GetMenuItems().Item(endGroup);
271 wxASSERT_MSG( node, wxS("Should have valid radio group end") );
272
273 while ( node->GetData() != item )
274 {
275 const wxMenuItemList::compatibility_iterator
276 prevNode = node->GetPrevious();
277 wxMenuItem* const prevItem = prevNode->GetData();
278 if ( prevItem == item )
279 {
280 prevItem->SetAsRadioGroupStart();
281 prevItem->SetRadioGroupEnd(endGroup);
282 break;
283 }
284
285 node = prevNode;
286 }
287 }
288 }
289
290 /*
291 // we need to find the items position in the child list
292 size_t pos;
293 wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
294
295 for ( pos = 0; node; pos++ )
296 {
297 if ( node->GetData() == item )
298 break;
299
300 node = node->GetNext();
301 }
302
303 // DoRemove() (unlike Remove) can only be called for existing item!
304 wxCHECK_MSG( node, NULL, wxT("bug in wxMenu::Remove logic") );
305
306 wxOSXMenuRemoveItem(m_hMenu , pos );
307 */
308 GetPeer()->Remove( item );
309 // and from internal data structures
310 return wxMenuBase::DoRemove(item);
311 }
312
313 void wxMenu::SetTitle(const wxString& label)
314 {
315 m_title = label ;
316 GetPeer()->SetTitle( wxStripMenuCodes( label ) );
317 }
318
319 bool wxMenu::ProcessCommand(wxCommandEvent & event)
320 {
321 bool processed = false;
322
323 // Try the menu's event handler
324 if ( /* !processed && */ GetEventHandler())
325 processed = GetEventHandler()->SafelyProcessEvent(event);
326
327 // Try the window the menu was popped up from
328 // (and up through the hierarchy)
329 wxWindow *win = GetWindow();
330 if ( !processed && win )
331 processed = win->HandleWindowEvent(event);
332
333 return processed;
334 }
335
336 // ---------------------------------------------------------------------------
337 // other
338 // ---------------------------------------------------------------------------
339
340 // MacOS needs to know about submenus somewhere within this menu
341 // before it can be displayed, also hide special menu items
342 // like preferences that are handled by the OS
343 void wxMenu::DoRearrange()
344 {
345 if ( !AllowRearrange() )
346 return;
347
348 wxMenuItem* previousItem = NULL ;
349 size_t pos ;
350 wxMenuItemList::compatibility_iterator node;
351 wxMenuItem *item;
352
353 for (pos = 0, node = GetMenuItems().GetFirst(); node; node = node->GetNext(), pos++)
354 {
355 item = (wxMenuItem *)node->GetData();
356 wxMenu* subMenu = item->GetSubMenu() ;
357 if (subMenu)
358 {
359 // already done
360 }
361 else // normal item
362 {
363 // what we do here is to hide the special items which are
364 // shown in the application menu anyhow -- it doesn't make
365 // sense to show them in their normal place as well
366 if ( item->GetId() == wxApp::s_macAboutMenuItemId ||
367 item->GetId() == wxApp::s_macPreferencesMenuItemId ||
368 item->GetId() == wxApp::s_macExitMenuItemId )
369
370 {
371 item->GetPeer()->Hide( true );
372
373 // also check for a separator which was used just to
374 // separate this item from the others, so don't leave
375 // separator at the menu start or end nor 2 consecutive
376 // separators
377 wxMenuItemList::compatibility_iterator nextNode = node->GetNext();
378 wxMenuItem *next = nextNode ? nextNode->GetData() : NULL;
379
380 wxMenuItem *sepToHide = 0;
381 if ( !previousItem && next && next->IsSeparator() )
382 {
383 // next (i.e. second as we must be first) item is
384 // the separator to hide
385 wxASSERT_MSG( pos == 0, wxT("should be the menu start") );
386 sepToHide = next;
387 }
388 else if ( GetMenuItems().GetCount() == pos + 1 &&
389 previousItem != NULL &&
390 previousItem->IsSeparator() )
391 {
392 // prev item is a trailing separator we want to hide
393 sepToHide = previousItem;
394 }
395 else if ( previousItem && previousItem->IsSeparator() &&
396 next && next->IsSeparator() )
397 {
398 // two consecutive separators, this is one too many
399 sepToHide = next;
400 }
401
402 if ( sepToHide )
403 {
404 // hide the separator as well
405 sepToHide->GetPeer()->Hide( true );
406 }
407 }
408 }
409
410 previousItem = item ;
411 }
412 }
413
414
415 bool wxMenu::HandleCommandUpdateStatus( wxMenuItem* item, wxWindow* senderWindow )
416 {
417 int menuid = item ? item->GetId() : 0;
418 wxUpdateUIEvent event(menuid);
419 event.SetEventObject( this );
420
421 bool processed = false;
422
423 // Try the menu's event handler
424 {
425 wxEvtHandler *handler = GetEventHandler();
426 if ( handler )
427 processed = handler->ProcessEvent(event);
428 }
429
430 // Try the window the menu was popped up from
431 // (and up through the hierarchy)
432 if ( !processed )
433 {
434 wxWindow *win = GetWindow();
435 if ( win )
436 processed = win->HandleWindowEvent(event);
437 }
438
439 if ( !processed && senderWindow != NULL)
440 {
441 processed = senderWindow->HandleWindowEvent(event);
442 }
443
444 if ( processed )
445 {
446 // if anything changed, update the changed attribute
447 if (event.GetSetText())
448 SetLabel(menuid, event.GetText());
449 if (event.GetSetChecked())
450 Check(menuid, event.GetChecked());
451 if (event.GetSetEnabled())
452 Enable(menuid, event.GetEnabled());
453 }
454 else
455 {
456 #if wxOSX_USE_CARBON
457 // these two items are also managed by the Carbon Menu Manager, therefore we must
458 // always reset them ourselves
459 UInt32 cmd = 0;
460
461 if ( menuid == wxApp::s_macExitMenuItemId )
462 {
463 cmd = kHICommandQuit;
464 }
465 else if (menuid == wxApp::s_macPreferencesMenuItemId )
466 {
467 cmd = kHICommandPreferences;
468 }
469
470 if ( cmd != 0 )
471 {
472 if ( !item->IsEnabled() || wxDialog::OSXHasModalDialogsOpen() )
473 DisableMenuCommand( NULL , cmd ) ;
474 else
475 EnableMenuCommand( NULL , cmd ) ;
476
477 }
478 #endif
479 }
480
481 return processed;
482 }
483
484 bool wxMenu::HandleCommandProcess( wxMenuItem* item, wxWindow* senderWindow )
485 {
486 int menuid = item ? item->GetId() : 0;
487 bool processed = false;
488 if (item->IsCheckable())
489 item->Check( !item->IsChecked() ) ;
490
491 if ( SendEvent( menuid , item->IsCheckable() ? item->IsChecked() : -1 ) )
492 processed = true ;
493 else
494 {
495 if ( senderWindow != NULL )
496 {
497 wxCommandEvent event(wxEVT_MENU , menuid);
498 event.SetEventObject(this);
499 event.SetInt(item->IsCheckable() ? item->IsChecked() : -1);
500
501 if ( senderWindow->HandleWindowEvent(event) )
502 processed = true ;
503 }
504 }
505
506 if(!processed && item)
507 {
508 processed = item->GetPeer()->DoDefault();
509 }
510
511 return processed;
512 }
513
514 void wxMenu::HandleMenuItemHighlighted( wxMenuItem* item )
515 {
516 int menuid = item ? item->GetId() : 0;
517 wxMenuEvent wxevent(wxEVT_MENU_HIGHLIGHT, menuid, this);
518 DoHandleMenuEvent( wxevent );
519 }
520
521 void wxMenu::DoHandleMenuOpenedOrClosed(wxEventType evtType)
522 {
523 // Popup menu being currently shown or NULL, defined in wincmn.cpp.
524 extern wxMenu *wxCurrentPopupMenu;
525
526 // Set the id to allow wxMenuEvent::IsPopup() to work correctly.
527 int menuid = this == wxCurrentPopupMenu ? wxID_ANY : 0;
528 wxMenuEvent wxevent(evtType, menuid, this);
529 DoHandleMenuEvent( wxevent );
530 }
531
532 void wxMenu::HandleMenuOpened()
533 {
534 DoHandleMenuOpenedOrClosed(wxEVT_MENU_OPEN);
535 }
536
537 void wxMenu::HandleMenuClosed()
538 {
539 DoHandleMenuOpenedOrClosed(wxEVT_MENU_CLOSE);
540 }
541
542 bool wxMenu::DoHandleMenuEvent(wxEvent& wxevent)
543 {
544 wxevent.SetEventObject(this);
545 wxEvtHandler* handler = GetEventHandler();
546 if (handler && handler->ProcessEvent(wxevent))
547 {
548 return true;
549 }
550 else
551 {
552 wxWindow *win = GetWindow();
553 if (win)
554 {
555 if ( win->HandleWindowEvent(wxevent) )
556 return true;
557 }
558 }
559 return false;
560 }
561
562 // Menu Bar
563
564 /*
565
566 Mac Implementation note :
567
568 The Mac has only one global menubar, so we attempt to install the currently
569 active menubar from a frame, we currently don't take into account mdi-frames
570 which would ask for menu-merging
571
572 Secondly there is no mac api for changing a menubar that is not the current
573 menubar, so we have to wait for preparing the actual menubar until the
574 wxMenubar is to be used
575
576 We can in subsequent versions use MacInstallMenuBar to provide some sort of
577 auto-merge for MDI in case this will be necessary
578
579 */
580
581 wxMenuBar* wxMenuBar::s_macInstalledMenuBar = NULL ;
582 wxMenuBar* wxMenuBar::s_macCommonMenuBar = NULL ;
583 bool wxMenuBar::s_macAutoWindowMenu = true ;
584 WXHMENU wxMenuBar::s_macWindowMenuHandle = NULL ;
585
586
587 wxMenu* emptyMenuBar = NULL;
588
589 const int firstMenuPos = 1; // to account for the 0th application menu on mac
590
591 void wxMenuBar::Init()
592 {
593 if ( emptyMenuBar == NULL )
594 {
595 emptyMenuBar = new wxMenu();
596
597 wxMenu* appleMenu = new wxMenu();
598 appleMenu->SetAllowRearrange(false);
599 #if !wxOSX_USE_CARBON
600 // standard menu items, handled in wxMenu::HandleCommandProcess(), see above:
601 wxString hideLabel;
602 hideLabel = wxString::Format(_("Hide %s"), wxTheApp ? wxTheApp->GetAppDisplayName() : _("Application"));
603 appleMenu->Append( wxID_OSX_HIDE, hideLabel + "\tCtrl+H" );
604 appleMenu->Append( wxID_OSX_HIDEOTHERS, _("Hide Others")+"\tAlt+Ctrl+H" );
605 appleMenu->Append( wxID_OSX_SHOWALL, _("Show All") );
606 appleMenu->AppendSeparator();
607
608 // Do always add "Quit" item unconditionally however, it can't be disabled.
609 wxString quitLabel;
610 quitLabel = wxString::Format(_("Quit %s"), wxTheApp ? wxTheApp->GetAppDisplayName() : _("Application"));
611 appleMenu->Append( wxApp::s_macExitMenuItemId, quitLabel + "\tCtrl+Q" );
612 #endif // !wxOSX_USE_CARBON
613
614 emptyMenuBar->AppendSubMenu(appleMenu, "\x14") ;
615 }
616
617 m_eventHandler = this;
618 m_menuBarFrame = NULL;
619 m_rootMenu = new wxMenu();
620 m_rootMenu->Attach(this);
621
622 m_appleMenu = new wxMenu();
623 m_appleMenu->SetAllowRearrange(false);
624
625 #if 0
626 // Create standard items unless the application explicitly disabled this by
627 // setting the corresponding ids to wxID_NONE: although this is not
628 // recommended, sometimes these items really don't make sense.
629 if ( wxApp::s_macAboutMenuItemId != wxID_NONE )
630 {
631 wxString aboutLabel(_("About"));
632 if ( wxTheApp )
633 aboutLabel << ' ' << wxTheApp->GetAppDisplayName();
634 else
635 aboutLabel << "...";
636 m_appleMenu->Append( wxApp::s_macAboutMenuItemId, aboutLabel);
637 m_appleMenu->AppendSeparator();
638 }
639
640 #if !wxOSX_USE_CARBON
641 if ( wxApp::s_macPreferencesMenuItemId != wxID_NONE )
642 {
643 m_appleMenu->Append( wxApp::s_macPreferencesMenuItemId,
644 _("Preferences...") + "\tCtrl+," );
645 m_appleMenu->AppendSeparator();
646 }
647
648 // standard menu items, handled in wxMenu::HandleCommandProcess(), see above:
649 wxString hideLabel;
650 hideLabel = wxString::Format(_("Hide %s"), wxTheApp ? wxTheApp->GetAppDisplayName() : _("Application"));
651 m_appleMenu->Append( wxID_OSX_HIDE, hideLabel + "\tCtrl+H" );
652 m_appleMenu->Append( wxID_OSX_HIDEOTHERS, _("Hide Others")+"\tAlt+Ctrl+H" );
653 m_appleMenu->Append( wxID_OSX_SHOWALL, _("Show All") );
654 m_appleMenu->AppendSeparator();
655
656 // Do always add "Quit" item unconditionally however, it can't be disabled.
657 wxString quitLabel;
658 quitLabel = wxString::Format(_("Quit %s"), wxTheApp ? wxTheApp->GetAppDisplayName() : _("Application"));
659 m_appleMenu->Append( wxApp::s_macExitMenuItemId, quitLabel + "\tCtrl+Q" );
660 #endif // !wxOSX_USE_CARBON
661 #endif
662
663 m_rootMenu->AppendSubMenu(m_appleMenu, "\x14") ;
664 }
665
666 wxMenuBar::wxMenuBar()
667 {
668 Init();
669 }
670
671 wxMenuBar::wxMenuBar( long WXUNUSED(style) )
672 {
673 Init();
674 }
675
676 wxMenuBar::wxMenuBar(size_t count, wxMenu *menus[], const wxString titles[], long WXUNUSED(style))
677 {
678 Init();
679
680 for ( size_t i = 0; i < count; i++ )
681 {
682 m_menus.Append(menus[i]);
683
684 menus[i]->Attach(this);
685 Append( menus[i], titles[i] );
686 }
687 }
688
689 wxMenuBar::~wxMenuBar()
690 {
691 if (s_macCommonMenuBar == this)
692 s_macCommonMenuBar = NULL;
693
694 if (s_macInstalledMenuBar == this)
695 {
696 emptyMenuBar->GetPeer()->MakeRoot();
697 s_macInstalledMenuBar = NULL;
698 }
699 wxDELETE( m_rootMenu );
700 // apple menu is a submenu, therefore we don't have to delete it
701 m_appleMenu = NULL;
702
703 // deleting the root menu also removes all its wxMenu* submenus, therefore
704 // we must avoid double deleting them in the superclass destructor
705 m_menus.clear();
706 }
707
708 void wxMenuBar::Refresh(bool WXUNUSED(eraseBackground), const wxRect *WXUNUSED(rect))
709 {
710 wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") );
711 }
712
713 void wxMenuBar::MacInstallMenuBar()
714 {
715 if ( s_macInstalledMenuBar == this )
716 return ;
717
718 m_rootMenu->GetPeer()->MakeRoot();
719
720 #if 0
721 // hide items in the apple menu that don't exist in the wx menubar
722
723 wxMenuItem* appleItem = NULL;
724 wxMenuItem* wxItem = NULL;
725
726 int menuid = wxApp::s_macAboutMenuItemId;
727 appleItem = m_appleMenu->FindItem(menuid);
728 wxItem = FindItem(menuid);
729 if ( appleItem != NULL )
730 {
731 if ( wxItem == NULL )
732 appleItem->GetPeer()->Hide();
733 else
734 appleItem->SetItemLabel(wxItem->GetItemLabel());
735 }
736
737 menuid = wxApp::s_macPreferencesMenuItemId;
738 appleItem = m_appleMenu->FindItem(menuid);
739 wxItem = FindItem(menuid);
740 if ( appleItem != NULL )
741 {
742 if ( wxItem == NULL )
743 appleItem->GetPeer()->Hide();
744 else
745 appleItem->SetItemLabel(wxItem->GetItemLabel());
746 }
747 #endif
748
749
750 #if 0
751
752 // if we have a mac help menu, clean it up before adding new items
753 MenuHandle helpMenuHandle ;
754 MenuItemIndex firstUserHelpMenuItem ;
755
756 if ( UMAGetHelpMenuDontCreate( &helpMenuHandle , &firstUserHelpMenuItem) == noErr )
757 {
758 for ( int i = CountMenuItems( helpMenuHandle ) ; i >= firstUserHelpMenuItem ; --i )
759 DeleteMenuItem( helpMenuHandle , i ) ;
760 }
761 else
762 {
763 helpMenuHandle = NULL ;
764 }
765
766 if ( wxApp::s_macPreferencesMenuItemId)
767 {
768 wxMenuItem *item = FindItem( wxApp::s_macPreferencesMenuItemId , NULL ) ;
769 if ( item == NULL || !(item->IsEnabled()) )
770 DisableMenuCommand( NULL , kHICommandPreferences ) ;
771 else
772 EnableMenuCommand( NULL , kHICommandPreferences ) ;
773 }
774
775 // Unlike preferences which may or may not exist, the Quit item should be always
776 // enabled unless it is added by the application and then disabled, otherwise
777 // a program would be required to add an item with wxID_EXIT in order to get the
778 // Quit menu item to be enabled, which seems a bit burdensome.
779 if ( wxApp::s_macExitMenuItemId)
780 {
781 wxMenuItem *item = FindItem( wxApp::s_macExitMenuItemId , NULL ) ;
782 if ( item != NULL && !(item->IsEnabled()) )
783 DisableMenuCommand( NULL , kHICommandQuit ) ;
784 else
785 EnableMenuCommand( NULL , kHICommandQuit ) ;
786 }
787
788 wxString strippedHelpMenuTitle = wxStripMenuCodes( wxApp::s_macHelpMenuTitleName ) ;
789 wxString strippedTranslatedHelpMenuTitle = wxStripMenuCodes( wxString( _("&Help") ) ) ;
790 wxMenuList::compatibility_iterator menuIter = m_menus.GetFirst();
791 for (size_t i = 0; i < m_menus.GetCount(); i++, menuIter = menuIter->GetNext())
792 {
793 wxMenuItemList::compatibility_iterator node;
794 wxMenuItem *item;
795 wxMenu* menu = menuIter->GetData() , *subMenu = NULL ;
796 wxString strippedMenuTitle = wxStripMenuCodes(m_titles[i]);
797
798 if ( strippedMenuTitle == wxT("?") || strippedMenuTitle == strippedHelpMenuTitle || strippedMenuTitle == strippedTranslatedHelpMenuTitle )
799 {
800 for (node = menu->GetMenuItems().GetFirst(); node; node = node->GetNext())
801 {
802 item = (wxMenuItem *)node->GetData();
803 subMenu = item->GetSubMenu() ;
804 if (subMenu)
805 {
806 UMAAppendMenuItem(mh, wxStripMenuCodes(item->GetText()) , wxFont::GetDefaultEncoding() );
807 MenuItemIndex position = CountMenuItems(mh);
808 ::SetMenuItemHierarchicalMenu(mh, position, MAC_WXHMENU(subMenu->GetHMenu()));
809 }
810 else
811 {
812 if ( item->GetId() != wxApp::s_macAboutMenuItemId )
813 {
814 // we have found a user help menu and an item other than the about item,
815 // so we can create the mac help menu now, if we haven't created it yet
816 if ( helpMenuHandle == NULL )
817 {
818 if ( UMAGetHelpMenu( &helpMenuHandle , &firstUserHelpMenuItem) != noErr )
819 {
820 helpMenuHandle = NULL ;
821 break ;
822 }
823 }
824 }
825
826 if ( item->IsSeparator() )
827 {
828 if ( helpMenuHandle )
829 AppendMenuItemTextWithCFString( helpMenuHandle,
830 CFSTR(""), kMenuItemAttrSeparator, 0,NULL);
831 }
832 else
833 {
834 wxAcceleratorEntry*
835 entry = wxAcceleratorEntry::Create( item->GetItemLabel() ) ;
836
837 if ( item->GetId() == wxApp::s_macAboutMenuItemId )
838 {
839 // this will be taken care of below
840 }
841 else
842 {
843 if ( helpMenuHandle )
844 {
845 UMAAppendMenuItem(helpMenuHandle, wxStripMenuCodes(item->GetItemLabel()) , wxFont::GetDefaultEncoding(), entry);
846 SetMenuItemCommandID( helpMenuHandle , CountMenuItems(helpMenuHandle) , wxIdToMacCommand ( item->GetId() ) ) ;
847 SetMenuItemRefCon( helpMenuHandle , CountMenuItems(helpMenuHandle) , (URefCon) item ) ;
848 }
849 }
850
851 delete entry ;
852 }
853 }
854 }
855 }
856
857 else if ( ( m_titles[i] == wxT("Window") || m_titles[i] == wxT("&Window") )
858 && GetAutoWindowMenu() )
859 {
860 if ( MacGetWindowMenuHMenu() == NULL )
861 {
862 CreateStandardWindowMenu( 0 , (MenuHandle*) &s_macWindowMenuHandle ) ;
863 }
864
865 MenuRef wm = (MenuRef)MacGetWindowMenuHMenu();
866 if ( wm == NULL )
867 break;
868
869 // get the insertion point in the standard menu
870 MenuItemIndex winListStart;
871 GetIndMenuItemWithCommandID(wm,
872 kHICommandWindowListSeparator, 1, NULL, &winListStart);
873
874 // add a separator so that the standard items and the custom items
875 // aren't mixed together, but only if this is the first run
876 OSStatus err = GetIndMenuItemWithCommandID(wm,
877 'WXWM', 1, NULL, NULL);
878
879 if ( err == menuItemNotFoundErr )
880 {
881 InsertMenuItemTextWithCFString( wm,
882 CFSTR(""), winListStart-1, kMenuItemAttrSeparator, 'WXWM');
883 }
884
885 wxInsertMenuItemsInMenu(menu, wm, winListStart);
886 }
887 else
888 {
889 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , m_titles[i], GetFont().GetEncoding() ) ;
890 menu->MacBeforeDisplay(false) ;
891
892 ::InsertMenu(MAC_WXHMENU(GetMenu(i)->GetHMenu()), 0);
893 }
894 }
895
896 // take care of the about menu item wherever it is
897 {
898 wxMenu* aboutMenu ;
899 wxMenuItem *aboutMenuItem = FindItem(wxApp::s_macAboutMenuItemId , &aboutMenu) ;
900 if ( aboutMenuItem )
901 {
902 wxAcceleratorEntry*
903 entry = wxAcceleratorEntry::Create( aboutMenuItem->GetItemLabel() ) ;
904 UMASetMenuItemText( GetMenuHandle( kwxMacAppleMenuId ) , 1 , wxStripMenuCodes ( aboutMenuItem->GetItemLabel() ) , wxFont::GetDefaultEncoding() );
905 UMAEnableMenuItem( GetMenuHandle( kwxMacAppleMenuId ) , 1 , true );
906 SetMenuItemCommandID( GetMenuHandle( kwxMacAppleMenuId ) , 1 , kHICommandAbout ) ;
907 SetMenuItemRefCon(GetMenuHandle( kwxMacAppleMenuId ) , 1 , (URefCon)aboutMenuItem ) ;
908 UMASetMenuItemShortcut( GetMenuHandle( kwxMacAppleMenuId ) , 1 , entry ) ;
909 delete entry;
910 }
911 }
912
913 if ( GetAutoWindowMenu() )
914 {
915 if ( MacGetWindowMenuHMenu() == NULL )
916 CreateStandardWindowMenu( 0 , (MenuHandle*) &s_macWindowMenuHandle ) ;
917
918 InsertMenu( (MenuHandle) MacGetWindowMenuHMenu() , 0 ) ;
919 }
920
921 ::DrawMenuBar() ;
922 #endif
923
924 s_macInstalledMenuBar = this;
925 }
926
927 void wxMenuBar::EnableTop(size_t pos, bool enable)
928 {
929 wxCHECK_RET( IsAttached(), wxT("doesn't work with unattached menubars") );
930
931 m_rootMenu->FindItemByPosition(pos+firstMenuPos)->Enable(enable);
932
933 Refresh();
934 }
935
936 bool wxMenuBar::IsEnabledTop(size_t pos) const
937 {
938 wxCHECK_MSG( IsAttached(), true,
939 wxT("doesn't work with unattached menubars") );
940
941 wxMenuItem* const item = m_rootMenu->FindItemByPosition(pos+firstMenuPos);
942 wxCHECK_MSG( item, false, wxT("invalid menu index") );
943
944 return item->IsEnabled();
945 }
946
947 bool wxMenuBar::Enable(bool enable)
948 {
949 wxCHECK_MSG( IsAttached(), false, wxT("doesn't work with unattached menubars") );
950
951 size_t i;
952 for (i = 0; i < GetMenuCount(); i++)
953 EnableTop(i, enable);
954
955 return true;
956 }
957
958 void wxMenuBar::SetMenuLabel(size_t pos, const wxString& label)
959 {
960 wxCHECK_RET( pos < GetMenuCount(), wxT("invalid menu index") );
961
962 GetMenu(pos)->SetTitle( label ) ;
963 }
964
965 wxString wxMenuBar::GetMenuLabel(size_t pos) const
966 {
967 wxCHECK_MSG( pos < GetMenuCount(), wxEmptyString,
968 wxT("invalid menu index in wxMenuBar::GetMenuLabel") );
969
970 return GetMenu(pos)->GetTitle();
971 }
972
973 // ---------------------------------------------------------------------------
974 // wxMenuBar construction
975 // ---------------------------------------------------------------------------
976
977 wxMenu *wxMenuBar::Replace(size_t pos, wxMenu *menu, const wxString& title)
978 {
979 wxMenu *menuOld = wxMenuBarBase::Replace(pos, menu, title);
980 if ( !menuOld )
981 return NULL;
982
983 wxMenuItem* item = m_rootMenu->FindItemByPosition(pos+firstMenuPos);
984 m_rootMenu->Remove(item);
985 m_rootMenu->Insert( pos+firstMenuPos, wxMenuItem::New( m_rootMenu, wxID_ANY, title, "", wxITEM_NORMAL, menu ) );
986
987 return menuOld;
988 }
989
990 bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title)
991 {
992 if ( !wxMenuBarBase::Insert(pos, menu, title) )
993 return false;
994
995 m_rootMenu->Insert( pos+firstMenuPos, wxMenuItem::New( m_rootMenu, wxID_ANY, title, "", wxITEM_NORMAL, menu ) );
996 menu->SetTitle(title);
997
998 return true;
999 }
1000
1001 wxMenu *wxMenuBar::Remove(size_t pos)
1002 {
1003 wxMenu *menu = wxMenuBarBase::Remove(pos);
1004 if ( !menu )
1005 return NULL;
1006
1007 wxMenuItem* item = m_rootMenu->FindItemByPosition(pos+firstMenuPos);
1008 m_rootMenu->Remove(item);
1009
1010 return menu;
1011 }
1012
1013 bool wxMenuBar::Append(wxMenu *menu, const wxString& title)
1014 {
1015 WXHMENU submenu = menu ? menu->GetHMenu() : 0;
1016 wxCHECK_MSG( submenu, false, wxT("can't append invalid menu to menubar") );
1017
1018 if ( !wxMenuBarBase::Append(menu, title) )
1019 return false;
1020
1021 m_rootMenu->AppendSubMenu(menu, title);
1022 menu->SetTitle(title);
1023
1024 return true;
1025 }
1026
1027 void wxMenuBar::Detach()
1028 {
1029 wxMenuBarBase::Detach() ;
1030 }
1031
1032 void wxMenuBar::Attach(wxFrame *frame)
1033 {
1034 wxMenuBarBase::Attach( frame ) ;
1035 }
1036
1037 #endif // wxUSE_MENUS