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