]> git.saurik.com Git - wxWidgets.git/blob - src/osx/menu_osx.cpp
Workaround for #15404: wxRichTextCtrl: caret does not disappear when focus is lost...
[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 // Create standard items unless the application explicitly disabled this by
626 // setting the corresponding ids to wxID_NONE: although this is not
627 // recommended, sometimes these items really don't make sense.
628 if ( wxApp::s_macAboutMenuItemId != wxID_NONE )
629 {
630 wxString aboutLabel(_("About"));
631 if ( wxTheApp )
632 aboutLabel << ' ' << wxTheApp->GetAppDisplayName();
633 else
634 aboutLabel << "...";
635 m_appleMenu->Append( wxApp::s_macAboutMenuItemId, aboutLabel);
636 m_appleMenu->AppendSeparator();
637 }
638
639 #if !wxOSX_USE_CARBON
640 if ( wxApp::s_macPreferencesMenuItemId != wxID_NONE )
641 {
642 m_appleMenu->Append( wxApp::s_macPreferencesMenuItemId,
643 _("Preferences...") + "\tCtrl+," );
644 m_appleMenu->AppendSeparator();
645 }
646
647 // standard menu items, handled in wxMenu::HandleCommandProcess(), see above:
648 wxString hideLabel;
649 hideLabel = wxString::Format(_("Hide %s"), wxTheApp ? wxTheApp->GetAppDisplayName() : _("Application"));
650 m_appleMenu->Append( wxID_OSX_HIDE, hideLabel + "\tCtrl+H" );
651 m_appleMenu->Append( wxID_OSX_HIDEOTHERS, _("Hide Others")+"\tAlt+Ctrl+H" );
652 m_appleMenu->Append( wxID_OSX_SHOWALL, _("Show All") );
653 m_appleMenu->AppendSeparator();
654
655 // Do always add "Quit" item unconditionally however, it can't be disabled.
656 wxString quitLabel;
657 quitLabel = wxString::Format(_("Quit %s"), wxTheApp ? wxTheApp->GetAppDisplayName() : _("Application"));
658 m_appleMenu->Append( wxApp::s_macExitMenuItemId, quitLabel + "\tCtrl+Q" );
659 #endif // !wxOSX_USE_CARBON
660
661 m_rootMenu->AppendSubMenu(m_appleMenu, "\x14") ;
662 }
663
664 wxMenuBar::wxMenuBar()
665 {
666 Init();
667 }
668
669 wxMenuBar::wxMenuBar( long WXUNUSED(style) )
670 {
671 Init();
672 }
673
674 wxMenuBar::wxMenuBar(size_t count, wxMenu *menus[], const wxString titles[], long WXUNUSED(style))
675 {
676 Init();
677
678 for ( size_t i = 0; i < count; i++ )
679 {
680 m_menus.Append(menus[i]);
681
682 menus[i]->Attach(this);
683 Append( menus[i], titles[i] );
684 }
685 }
686
687 wxMenuBar::~wxMenuBar()
688 {
689 if (s_macCommonMenuBar == this)
690 s_macCommonMenuBar = NULL;
691
692 if (s_macInstalledMenuBar == this)
693 {
694 emptyMenuBar->GetPeer()->MakeRoot();
695 s_macInstalledMenuBar = NULL;
696 }
697 wxDELETE( m_rootMenu );
698 // apple menu is a submenu, therefore we don't have to delete it
699 m_appleMenu = NULL;
700
701 // deleting the root menu also removes all its wxMenu* submenus, therefore
702 // we must avoid double deleting them in the superclass destructor
703 m_menus.clear();
704 }
705
706 void wxMenuBar::Refresh(bool WXUNUSED(eraseBackground), const wxRect *WXUNUSED(rect))
707 {
708 wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") );
709 }
710
711 void wxMenuBar::MacInstallMenuBar()
712 {
713 if ( s_macInstalledMenuBar == this )
714 return ;
715
716 m_rootMenu->GetPeer()->MakeRoot();
717
718 // hide items in the apple menu that don't exist in the wx menubar
719
720 wxMenuItem* appleItem = NULL;
721 wxMenuItem* wxItem = NULL;
722
723 int menuid = wxApp::s_macAboutMenuItemId;
724 appleItem = m_appleMenu->FindItem(menuid);
725 wxItem = FindItem(menuid);
726 if ( appleItem != NULL )
727 {
728 if ( wxItem == NULL )
729 appleItem->GetPeer()->Hide();
730 else
731 appleItem->SetItemLabel(wxItem->GetItemLabel());
732 }
733
734 menuid = wxApp::s_macPreferencesMenuItemId;
735 appleItem = m_appleMenu->FindItem(menuid);
736 wxItem = FindItem(menuid);
737 if ( appleItem != NULL )
738 {
739 if ( wxItem == NULL )
740 appleItem->GetPeer()->Hide();
741 else
742 appleItem->SetItemLabel(wxItem->GetItemLabel());
743 }
744
745
746 #if 0
747
748 // if we have a mac help menu, clean it up before adding new items
749 MenuHandle helpMenuHandle ;
750 MenuItemIndex firstUserHelpMenuItem ;
751
752 if ( UMAGetHelpMenuDontCreate( &helpMenuHandle , &firstUserHelpMenuItem) == noErr )
753 {
754 for ( int i = CountMenuItems( helpMenuHandle ) ; i >= firstUserHelpMenuItem ; --i )
755 DeleteMenuItem( helpMenuHandle , i ) ;
756 }
757 else
758 {
759 helpMenuHandle = NULL ;
760 }
761
762 if ( wxApp::s_macPreferencesMenuItemId)
763 {
764 wxMenuItem *item = FindItem( wxApp::s_macPreferencesMenuItemId , NULL ) ;
765 if ( item == NULL || !(item->IsEnabled()) )
766 DisableMenuCommand( NULL , kHICommandPreferences ) ;
767 else
768 EnableMenuCommand( NULL , kHICommandPreferences ) ;
769 }
770
771 // Unlike preferences which may or may not exist, the Quit item should be always
772 // enabled unless it is added by the application and then disabled, otherwise
773 // a program would be required to add an item with wxID_EXIT in order to get the
774 // Quit menu item to be enabled, which seems a bit burdensome.
775 if ( wxApp::s_macExitMenuItemId)
776 {
777 wxMenuItem *item = FindItem( wxApp::s_macExitMenuItemId , NULL ) ;
778 if ( item != NULL && !(item->IsEnabled()) )
779 DisableMenuCommand( NULL , kHICommandQuit ) ;
780 else
781 EnableMenuCommand( NULL , kHICommandQuit ) ;
782 }
783
784 wxString strippedHelpMenuTitle = wxStripMenuCodes( wxApp::s_macHelpMenuTitleName ) ;
785 wxString strippedTranslatedHelpMenuTitle = wxStripMenuCodes( wxString( _("&Help") ) ) ;
786 wxMenuList::compatibility_iterator menuIter = m_menus.GetFirst();
787 for (size_t i = 0; i < m_menus.GetCount(); i++, menuIter = menuIter->GetNext())
788 {
789 wxMenuItemList::compatibility_iterator node;
790 wxMenuItem *item;
791 wxMenu* menu = menuIter->GetData() , *subMenu = NULL ;
792 wxString strippedMenuTitle = wxStripMenuCodes(m_titles[i]);
793
794 if ( strippedMenuTitle == wxT("?") || strippedMenuTitle == strippedHelpMenuTitle || strippedMenuTitle == strippedTranslatedHelpMenuTitle )
795 {
796 for (node = menu->GetMenuItems().GetFirst(); node; node = node->GetNext())
797 {
798 item = (wxMenuItem *)node->GetData();
799 subMenu = item->GetSubMenu() ;
800 if (subMenu)
801 {
802 UMAAppendMenuItem(mh, wxStripMenuCodes(item->GetText()) , wxFont::GetDefaultEncoding() );
803 MenuItemIndex position = CountMenuItems(mh);
804 ::SetMenuItemHierarchicalMenu(mh, position, MAC_WXHMENU(subMenu->GetHMenu()));
805 }
806 else
807 {
808 if ( item->GetId() != wxApp::s_macAboutMenuItemId )
809 {
810 // we have found a user help menu and an item other than the about item,
811 // so we can create the mac help menu now, if we haven't created it yet
812 if ( helpMenuHandle == NULL )
813 {
814 if ( UMAGetHelpMenu( &helpMenuHandle , &firstUserHelpMenuItem) != noErr )
815 {
816 helpMenuHandle = NULL ;
817 break ;
818 }
819 }
820 }
821
822 if ( item->IsSeparator() )
823 {
824 if ( helpMenuHandle )
825 AppendMenuItemTextWithCFString( helpMenuHandle,
826 CFSTR(""), kMenuItemAttrSeparator, 0,NULL);
827 }
828 else
829 {
830 wxAcceleratorEntry*
831 entry = wxAcceleratorEntry::Create( item->GetItemLabel() ) ;
832
833 if ( item->GetId() == wxApp::s_macAboutMenuItemId )
834 {
835 // this will be taken care of below
836 }
837 else
838 {
839 if ( helpMenuHandle )
840 {
841 UMAAppendMenuItem(helpMenuHandle, wxStripMenuCodes(item->GetItemLabel()) , wxFont::GetDefaultEncoding(), entry);
842 SetMenuItemCommandID( helpMenuHandle , CountMenuItems(helpMenuHandle) , wxIdToMacCommand ( item->GetId() ) ) ;
843 SetMenuItemRefCon( helpMenuHandle , CountMenuItems(helpMenuHandle) , (URefCon) item ) ;
844 }
845 }
846
847 delete entry ;
848 }
849 }
850 }
851 }
852
853 else if ( ( m_titles[i] == wxT("Window") || m_titles[i] == wxT("&Window") )
854 && GetAutoWindowMenu() )
855 {
856 if ( MacGetWindowMenuHMenu() == NULL )
857 {
858 CreateStandardWindowMenu( 0 , (MenuHandle*) &s_macWindowMenuHandle ) ;
859 }
860
861 MenuRef wm = (MenuRef)MacGetWindowMenuHMenu();
862 if ( wm == NULL )
863 break;
864
865 // get the insertion point in the standard menu
866 MenuItemIndex winListStart;
867 GetIndMenuItemWithCommandID(wm,
868 kHICommandWindowListSeparator, 1, NULL, &winListStart);
869
870 // add a separator so that the standard items and the custom items
871 // aren't mixed together, but only if this is the first run
872 OSStatus err = GetIndMenuItemWithCommandID(wm,
873 'WXWM', 1, NULL, NULL);
874
875 if ( err == menuItemNotFoundErr )
876 {
877 InsertMenuItemTextWithCFString( wm,
878 CFSTR(""), winListStart-1, kMenuItemAttrSeparator, 'WXWM');
879 }
880
881 wxInsertMenuItemsInMenu(menu, wm, winListStart);
882 }
883 else
884 {
885 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , m_titles[i], GetFont().GetEncoding() ) ;
886 menu->MacBeforeDisplay(false) ;
887
888 ::InsertMenu(MAC_WXHMENU(GetMenu(i)->GetHMenu()), 0);
889 }
890 }
891
892 // take care of the about menu item wherever it is
893 {
894 wxMenu* aboutMenu ;
895 wxMenuItem *aboutMenuItem = FindItem(wxApp::s_macAboutMenuItemId , &aboutMenu) ;
896 if ( aboutMenuItem )
897 {
898 wxAcceleratorEntry*
899 entry = wxAcceleratorEntry::Create( aboutMenuItem->GetItemLabel() ) ;
900 UMASetMenuItemText( GetMenuHandle( kwxMacAppleMenuId ) , 1 , wxStripMenuCodes ( aboutMenuItem->GetItemLabel() ) , wxFont::GetDefaultEncoding() );
901 UMAEnableMenuItem( GetMenuHandle( kwxMacAppleMenuId ) , 1 , true );
902 SetMenuItemCommandID( GetMenuHandle( kwxMacAppleMenuId ) , 1 , kHICommandAbout ) ;
903 SetMenuItemRefCon(GetMenuHandle( kwxMacAppleMenuId ) , 1 , (URefCon)aboutMenuItem ) ;
904 UMASetMenuItemShortcut( GetMenuHandle( kwxMacAppleMenuId ) , 1 , entry ) ;
905 delete entry;
906 }
907 }
908
909 if ( GetAutoWindowMenu() )
910 {
911 if ( MacGetWindowMenuHMenu() == NULL )
912 CreateStandardWindowMenu( 0 , (MenuHandle*) &s_macWindowMenuHandle ) ;
913
914 InsertMenu( (MenuHandle) MacGetWindowMenuHMenu() , 0 ) ;
915 }
916
917 ::DrawMenuBar() ;
918 #endif
919
920 s_macInstalledMenuBar = this;
921 }
922
923 void wxMenuBar::EnableTop(size_t pos, bool enable)
924 {
925 wxCHECK_RET( IsAttached(), wxT("doesn't work with unattached menubars") );
926
927 m_rootMenu->FindItemByPosition(pos+firstMenuPos)->Enable(enable);
928
929 Refresh();
930 }
931
932 bool wxMenuBar::IsEnabledTop(size_t pos) const
933 {
934 wxCHECK_MSG( IsAttached(), true,
935 wxT("doesn't work with unattached menubars") );
936
937 wxMenuItem* const item = m_rootMenu->FindItemByPosition(pos+firstMenuPos);
938 wxCHECK_MSG( item, false, wxT("invalid menu index") );
939
940 return item->IsEnabled();
941 }
942
943 bool wxMenuBar::Enable(bool enable)
944 {
945 wxCHECK_MSG( IsAttached(), false, wxT("doesn't work with unattached menubars") );
946
947 size_t i;
948 for (i = 0; i < GetMenuCount(); i++)
949 EnableTop(i, enable);
950
951 return true;
952 }
953
954 void wxMenuBar::SetMenuLabel(size_t pos, const wxString& label)
955 {
956 wxCHECK_RET( pos < GetMenuCount(), wxT("invalid menu index") );
957
958 GetMenu(pos)->SetTitle( label ) ;
959 }
960
961 wxString wxMenuBar::GetMenuLabel(size_t pos) const
962 {
963 wxCHECK_MSG( pos < GetMenuCount(), wxEmptyString,
964 wxT("invalid menu index in wxMenuBar::GetMenuLabel") );
965
966 return GetMenu(pos)->GetTitle();
967 }
968
969 // ---------------------------------------------------------------------------
970 // wxMenuBar construction
971 // ---------------------------------------------------------------------------
972
973 wxMenu *wxMenuBar::Replace(size_t pos, wxMenu *menu, const wxString& title)
974 {
975 wxMenu *menuOld = wxMenuBarBase::Replace(pos, menu, title);
976 if ( !menuOld )
977 return NULL;
978
979 wxMenuItem* item = m_rootMenu->FindItemByPosition(pos+firstMenuPos);
980 m_rootMenu->Remove(item);
981 m_rootMenu->Insert( pos+firstMenuPos, wxMenuItem::New( m_rootMenu, wxID_ANY, title, "", wxITEM_NORMAL, menu ) );
982
983 return menuOld;
984 }
985
986 bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title)
987 {
988 if ( !wxMenuBarBase::Insert(pos, menu, title) )
989 return false;
990
991 m_rootMenu->Insert( pos+firstMenuPos, wxMenuItem::New( m_rootMenu, wxID_ANY, title, "", wxITEM_NORMAL, menu ) );
992 menu->SetTitle(title);
993
994 return true;
995 }
996
997 wxMenu *wxMenuBar::Remove(size_t pos)
998 {
999 wxMenu *menu = wxMenuBarBase::Remove(pos);
1000 if ( !menu )
1001 return NULL;
1002
1003 wxMenuItem* item = m_rootMenu->FindItemByPosition(pos+firstMenuPos);
1004 m_rootMenu->Remove(item);
1005
1006 return menu;
1007 }
1008
1009 bool wxMenuBar::Append(wxMenu *menu, const wxString& title)
1010 {
1011 WXHMENU submenu = menu ? menu->GetHMenu() : 0;
1012 wxCHECK_MSG( submenu, false, wxT("can't append invalid menu to menubar") );
1013
1014 if ( !wxMenuBarBase::Append(menu, title) )
1015 return false;
1016
1017 m_rootMenu->AppendSubMenu(menu, title);
1018 menu->SetTitle(title);
1019
1020 return true;
1021 }
1022
1023 void wxMenuBar::Detach()
1024 {
1025 wxMenuBarBase::Detach() ;
1026 }
1027
1028 void wxMenuBar::Attach(wxFrame *frame)
1029 {
1030 wxMenuBarBase::Attach( frame ) ;
1031 }
1032
1033 #endif // wxUSE_MENUS