]> git.saurik.com Git - wxWidgets.git/blob - src/mac/carbon/menu.cpp
b717e52e6bd193c9add602e17d01f3280ad7d1a9
[wxWidgets.git] / src / mac / carbon / menu.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/mac/carbon/menu.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 #include "wx/menu.h"
22
23 #ifndef WX_PRECOMP
24 #include "wx/log.h"
25 #include "wx/app.h"
26 #include "wx/utils.h"
27 #include "wx/frame.h"
28 #include "wx/menuitem.h"
29 #endif
30
31 #include "wx/mac/uma.h"
32
33 // other standard headers
34 // ----------------------
35 #include <string.h>
36
37 IMPLEMENT_DYNAMIC_CLASS(wxMenu, wxEvtHandler)
38 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar, wxEvtHandler)
39
40 // the (popup) menu title has this special id
41 static const int idMenuTitle = -3;
42
43 static const short kwxMacAppleMenuId = 1 ;
44
45
46 // Find an item given the Macintosh Menu Reference
47
48 WX_DECLARE_HASH_MAP(MenuRef, wxMenu*, wxPointerHash, wxPointerEqual, MacMenuMap);
49
50 static MacMenuMap wxWinMacMenuList;
51
52 wxMenu *wxFindMenuFromMacMenu(MenuRef inMenuRef)
53 {
54 MacMenuMap::iterator node = wxWinMacMenuList.find(inMenuRef);
55
56 return (node == wxWinMacMenuList.end()) ? NULL : node->second;
57 }
58
59 void wxAssociateMenuWithMacMenu(MenuRef inMenuRef, wxMenu *menu) ;
60 void wxAssociateMenuWithMacMenu(MenuRef inMenuRef, wxMenu *menu)
61 {
62 // adding NULL MenuRef is (first) surely a result of an error and
63 // (secondly) breaks menu command processing
64 wxCHECK_RET( inMenuRef != (MenuRef) NULL, wxT("attempt to add a NULL MenuRef to menu list") );
65
66 wxWinMacMenuList[inMenuRef] = menu;
67 }
68
69 void wxRemoveMacMenuAssociation(wxMenu *menu) ;
70 void wxRemoveMacMenuAssociation(wxMenu *menu)
71 {
72 // iterate over all the elements in the class
73 MacMenuMap::iterator it;
74 for ( it = wxWinMacMenuList.begin(); it != wxWinMacMenuList.end(); ++it )
75 {
76 if ( it->second == menu )
77 {
78 wxWinMacMenuList.erase(it);
79 break;
80 }
81 }
82 }
83
84 void wxInsertMenuItemsInMenu(wxMenu* menu, MenuRef wm, MenuItemIndex insertAfter)
85 {
86 wxMenuItemList::compatibility_iterator node;
87 wxMenuItem *item;
88 wxMenu *subMenu = NULL ;
89 bool newItems = false;
90
91 for (node = menu->GetMenuItems().GetFirst(); node; node = node->GetNext())
92 {
93 item = (wxMenuItem *)node->GetData();
94 subMenu = item->GetSubMenu() ;
95 if (subMenu)
96 {
97 wxInsertMenuItemsInMenu(subMenu, (MenuRef)subMenu->GetHMenu(), 0);
98 }
99 if ( item->IsSeparator() )
100 {
101 if ( wm && newItems)
102 InsertMenuItemTextWithCFString( wm,
103 CFSTR(""), insertAfter, kMenuItemAttrSeparator, 0);
104
105 newItems = false;
106 }
107 else
108 {
109 wxAcceleratorEntry*
110 entry = wxAcceleratorEntry::Create( item->GetItemLabel() ) ;
111
112 MenuItemIndex winListPos = (MenuItemIndex)-1;
113 OSStatus err = GetIndMenuItemWithCommandID(wm,
114 wxIdToMacCommand ( item->GetId() ), 1, NULL, &winListPos);
115
116 if ( wm && err == menuItemNotFoundErr )
117 {
118 // NB: the only way to determine whether or not we should add
119 // a separator is to know if we've added menu items to the menu
120 // before the separator.
121 newItems = true;
122 UMAInsertMenuItem(wm, wxStripMenuCodes(item->GetItemLabel()) , wxFont::GetDefaultEncoding(), insertAfter, entry);
123 SetMenuItemCommandID( wm , insertAfter+1 , wxIdToMacCommand ( item->GetId() ) ) ;
124 SetMenuItemRefCon( wm , insertAfter+1 , (URefCon) item ) ;
125 }
126
127 delete entry ;
128 }
129 }
130 }
131
132 // ============================================================================
133 // implementation
134 // ============================================================================
135 static void wxMenubarUnsetInvokingWindow( wxMenu *menu ) ;
136 static void wxMenubarSetInvokingWindow( wxMenu *menu, wxWindow *win );
137
138 // Menus
139
140 // Construct a menu with optional title (then use append)
141
142 #ifdef __DARWIN__
143 short wxMenu::s_macNextMenuId = 3 ;
144 #else
145 short wxMenu::s_macNextMenuId = 2 ;
146 #endif
147
148 static
149 wxMenu *
150 _wxMenuAt(const wxMenuList &menuList, size_t pos)
151 {
152 wxMenuList::compatibility_iterator menuIter = menuList.GetFirst();
153
154 while (pos-- > 0)
155 menuIter = menuIter->GetNext();
156
157 return menuIter->GetData() ;
158 }
159
160 void wxMenu::Init()
161 {
162 m_doBreak = false;
163 m_startRadioGroup = -1;
164
165 // create the menu
166 m_macMenuId = s_macNextMenuId++;
167 m_hMenu = UMANewMenu(m_macMenuId, m_title, wxFont::GetDefaultEncoding() );
168
169 if ( !m_hMenu )
170 {
171 wxLogLastError(wxT("UMANewMenu failed"));
172 }
173
174 wxAssociateMenuWithMacMenu( (MenuRef)m_hMenu , this ) ;
175
176 // if we have a title, insert it in the beginning of the menu
177 if ( !m_title.empty() )
178 {
179 Append(idMenuTitle, m_title) ;
180 AppendSeparator() ;
181 }
182 }
183
184 wxMenu::~wxMenu()
185 {
186 wxRemoveMacMenuAssociation( this ) ;
187 if (MAC_WXHMENU(m_hMenu))
188 ::DisposeMenu(MAC_WXHMENU(m_hMenu));
189 }
190
191 void wxMenu::Break()
192 {
193 // not available on the mac platform
194 }
195
196 void wxMenu::Attach(wxMenuBarBase *menubar)
197 {
198 wxMenuBase::Attach(menubar);
199
200 EndRadioGroup();
201 }
202
203 // function appends a new item or submenu to the menu
204 // append a new item or submenu to the menu
205 bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos)
206 {
207 wxASSERT_MSG( pItem != NULL, wxT("can't append NULL item to the menu") );
208
209 if ( pItem->IsSeparator() )
210 {
211 if ( pos == (size_t)-1 )
212 AppendMenuItemTextWithCFString( MAC_WXHMENU(m_hMenu),
213 CFSTR(""), kMenuItemAttrSeparator, 0,NULL);
214 else
215 InsertMenuItemTextWithCFString( MAC_WXHMENU(m_hMenu),
216 CFSTR(""), pos, kMenuItemAttrSeparator, 0);
217 }
218 else
219 {
220 wxMenu *pSubMenu = pItem->GetSubMenu() ;
221 if ( pSubMenu != NULL )
222 {
223 wxASSERT_MSG( pSubMenu->m_hMenu != NULL , wxT("invalid submenu added"));
224 pSubMenu->m_menuParent = this ;
225
226 if (wxMenuBar::MacGetInstalledMenuBar() == GetMenuBar())
227 pSubMenu->MacBeforeDisplay( true ) ;
228
229 if ( pos == (size_t)-1 )
230 UMAAppendSubMenuItem(MAC_WXHMENU(m_hMenu), wxStripMenuCodes(pItem->GetItemLabel()), wxFont::GetDefaultEncoding(), pSubMenu->m_macMenuId);
231 else
232 UMAInsertSubMenuItem(MAC_WXHMENU(m_hMenu), wxStripMenuCodes(pItem->GetItemLabel()), wxFont::GetDefaultEncoding(), pos, pSubMenu->m_macMenuId);
233
234 pItem->UpdateItemBitmap() ;
235 pItem->UpdateItemStatus() ;
236 }
237 else
238 {
239 if ( pos == (size_t)-1 )
240 {
241 UMAAppendMenuItem(MAC_WXHMENU(m_hMenu), wxT("a") , wxFont::GetDefaultEncoding() );
242 pos = CountMenuItems(MAC_WXHMENU(m_hMenu)) ;
243 }
244 else
245 {
246 // MacOS counts menu items from 1 and inserts after, therefore having the
247 // same effect as wx 0 based and inserting before, we must correct pos
248 // after however for updates to be correct
249 UMAInsertMenuItem(MAC_WXHMENU(m_hMenu), wxT("a"), wxFont::GetDefaultEncoding(), pos);
250 pos += 1 ;
251 }
252
253 SetMenuItemCommandID( MAC_WXHMENU(m_hMenu) , pos , wxIdToMacCommand ( pItem->GetId() ) ) ;
254 SetMenuItemRefCon( MAC_WXHMENU(m_hMenu) , pos , (URefCon) pItem ) ;
255 pItem->UpdateItemText() ;
256 pItem->UpdateItemBitmap() ;
257 pItem->UpdateItemStatus() ;
258
259 if ( pItem->GetId() == idMenuTitle )
260 UMAEnableMenuItem(MAC_WXHMENU(m_hMenu) , pos , false ) ;
261 }
262 }
263
264 // if we're already attached to the menubar, we must update it
265 if ( IsAttached() && GetMenuBar()->IsAttached() )
266 GetMenuBar()->Refresh();
267
268 return true ;
269 }
270
271 void wxMenu::EndRadioGroup()
272 {
273 // we're not inside a radio group any longer
274 m_startRadioGroup = -1;
275 }
276
277 wxMenuItem* wxMenu::DoAppend(wxMenuItem *item)
278 {
279 wxCHECK_MSG( item, NULL, _T("NULL item in wxMenu::DoAppend") );
280
281 bool check = false;
282
283 if ( item->GetKind() == wxITEM_RADIO )
284 {
285 int count = GetMenuItemCount();
286
287 if ( m_startRadioGroup == -1 )
288 {
289 // start a new radio group
290 m_startRadioGroup = count;
291
292 // for now it has just one element
293 item->SetAsRadioGroupStart();
294 item->SetRadioGroupEnd(m_startRadioGroup);
295
296 // ensure that we have a checked item in the radio group
297 check = true;
298 }
299 else // extend the current radio group
300 {
301 // we need to update its end item
302 item->SetRadioGroupStart(m_startRadioGroup);
303 wxMenuItemList::compatibility_iterator node = GetMenuItems().Item(m_startRadioGroup);
304
305 if ( node )
306 {
307 node->GetData()->SetRadioGroupEnd(count);
308 }
309 else
310 {
311 wxFAIL_MSG( _T("where is the radio group start item?") );
312 }
313 }
314 }
315 else // not a radio item
316 {
317 EndRadioGroup();
318 }
319
320 if ( !wxMenuBase::DoAppend(item) || !DoInsertOrAppend(item) )
321 return NULL;
322
323 if ( check )
324 // check the item initially
325 item->Check(true);
326
327 return item;
328 }
329
330 wxMenuItem* wxMenu::DoInsert(size_t pos, wxMenuItem *item)
331 {
332 if (wxMenuBase::DoInsert(pos, item) && DoInsertOrAppend(item, pos))
333 return item;
334
335 return NULL;
336 }
337
338 wxMenuItem *wxMenu::DoRemove(wxMenuItem *item)
339 {
340 // we need to find the items position in the child list
341 size_t pos;
342 wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
343
344 for ( pos = 0; node; pos++ )
345 {
346 if ( node->GetData() == item )
347 break;
348
349 node = node->GetNext();
350 }
351
352 // DoRemove() (unlike Remove) can only be called for existing item!
353 wxCHECK_MSG( node, NULL, wxT("bug in wxMenu::Remove logic") );
354
355 ::DeleteMenuItem(MAC_WXHMENU(m_hMenu) , pos + 1);
356
357 if ( IsAttached() && GetMenuBar()->IsAttached() )
358 // otherwise, the change won't be visible
359 GetMenuBar()->Refresh();
360
361 // and from internal data structures
362 return wxMenuBase::DoRemove(item);
363 }
364
365 void wxMenu::SetTitle(const wxString& label)
366 {
367 m_title = label ;
368 UMASetMenuTitle(MAC_WXHMENU(m_hMenu) , label , wxFont::GetDefaultEncoding() ) ;
369 }
370
371 bool wxMenu::ProcessCommand(wxCommandEvent & event)
372 {
373 bool processed = false;
374
375 // Try the menu's event handler
376 if ( /* !processed && */ GetEventHandler())
377 processed = GetEventHandler()->ProcessEvent(event);
378
379 // Try the window the menu was popped up from
380 // (and up through the hierarchy)
381 wxWindow *win = GetInvokingWindow();
382 if ( !processed && win )
383 processed = win->GetEventHandler()->ProcessEvent(event);
384
385 return processed;
386 }
387
388 // ---------------------------------------------------------------------------
389 // other
390 // ---------------------------------------------------------------------------
391
392 wxWindow *wxMenu::GetWindow() const
393 {
394 if ( m_invokingWindow != NULL )
395 return m_invokingWindow;
396 else if ( GetMenuBar() != NULL)
397 return (wxWindow *) GetMenuBar()->GetFrame();
398
399 return NULL;
400 }
401
402 // helper functions returning the mac menu position for a certain item, note that this is
403 // mac-wise 1 - based, i.e. the first item has index 1 whereas on MSWin it has pos 0
404
405 int wxMenu::MacGetIndexFromId( int id )
406 {
407 size_t pos;
408 wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
409 for ( pos = 0; node; pos++ )
410 {
411 if ( node->GetData()->GetId() == id )
412 break;
413
414 node = node->GetNext();
415 }
416
417 if (!node)
418 return 0;
419
420 return pos + 1 ;
421 }
422
423 int wxMenu::MacGetIndexFromItem( wxMenuItem *pItem )
424 {
425 size_t pos;
426 wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
427 for ( pos = 0; node; pos++ )
428 {
429 if ( node->GetData() == pItem )
430 break;
431
432 node = node->GetNext();
433 }
434
435 if (!node)
436 return 0;
437
438 return pos + 1 ;
439 }
440
441 void wxMenu::MacEnableMenu( bool bDoEnable )
442 {
443 UMAEnableMenuItem(MAC_WXHMENU(m_hMenu) , 0 , bDoEnable ) ;
444
445 ::DrawMenuBar() ;
446 }
447
448 // MacOS needs to know about submenus somewhere within this menu
449 // before it can be displayed, also hide special menu items
450 // like preferences that are handled by the OS
451 void wxMenu::MacBeforeDisplay( bool isSubMenu )
452 {
453 wxMenuItem* previousItem = NULL ;
454 size_t pos ;
455 wxMenuItemList::compatibility_iterator node;
456 wxMenuItem *item;
457
458 for (pos = 0, node = GetMenuItems().GetFirst(); node; node = node->GetNext(), pos++)
459 {
460 item = (wxMenuItem *)node->GetData();
461 wxMenu* subMenu = item->GetSubMenu() ;
462 if (subMenu)
463 {
464 subMenu->MacBeforeDisplay( true ) ;
465 }
466 else // normal item
467 {
468 // what we do here is to hide the special items which are
469 // shown in the application menu anyhow -- it doesn't make
470 // sense to show them in their normal place as well
471 if ( item->GetId() == wxApp::s_macAboutMenuItemId ||
472 ( UMAGetSystemVersion() >= 0x1000 && (
473 item->GetId() == wxApp::s_macPreferencesMenuItemId ||
474 item->GetId() == wxApp::s_macExitMenuItemId ) ) )
475
476 {
477 ChangeMenuItemAttributes( MAC_WXHMENU( GetHMenu() ),
478 pos + 1, kMenuItemAttrHidden, 0 );
479
480 // also check for a separator which was used just to
481 // separate this item from the others, so don't leave
482 // separator at the menu start or end nor 2 consecutive
483 // separators
484 wxMenuItemList::compatibility_iterator nextNode = node->GetNext();
485 wxMenuItem *next = nextNode ? nextNode->GetData() : NULL;
486
487 size_t posSeptoHide;
488 if ( !previousItem && next && next->IsSeparator() )
489 {
490 // next (i.e. second as we must be first) item is
491 // the separator to hide
492 wxASSERT_MSG( pos == 0, _T("should be the menu start") );
493 posSeptoHide = 2;
494 }
495 else if ( GetMenuItems().GetCount() == pos + 1 &&
496 previousItem != NULL &&
497 previousItem->IsSeparator() )
498 {
499 // prev item is a trailing separator we want to hide
500 posSeptoHide = pos;
501 }
502 else if ( previousItem && previousItem->IsSeparator() &&
503 next && next->IsSeparator() )
504 {
505 // two consecutive separators, this is one too many
506 posSeptoHide = pos;
507 }
508 else // no separators to hide
509 {
510 posSeptoHide = 0;
511 }
512
513 if ( posSeptoHide )
514 {
515 // hide the separator as well
516 ChangeMenuItemAttributes( MAC_WXHMENU( GetHMenu() ),
517 posSeptoHide,
518 kMenuItemAttrHidden,
519 0 );
520 }
521 }
522 }
523
524 previousItem = item ;
525 }
526
527 if ( isSubMenu )
528 ::InsertMenu(MAC_WXHMENU( GetHMenu()), -1);
529 }
530
531 // undo all changes from the MacBeforeDisplay call
532 void wxMenu::MacAfterDisplay( bool isSubMenu )
533 {
534 if ( isSubMenu )
535 ::DeleteMenu(MacGetMenuId());
536
537 wxMenuItemList::compatibility_iterator node;
538 wxMenuItem *item;
539
540 for (node = GetMenuItems().GetFirst(); node; node = node->GetNext())
541 {
542 item = (wxMenuItem *)node->GetData();
543 wxMenu* subMenu = item->GetSubMenu() ;
544 if (subMenu)
545 {
546 subMenu->MacAfterDisplay( true ) ;
547 }
548 else
549 {
550 // no need to undo hidings
551 }
552 }
553 }
554
555 wxInt32 wxMenu::MacHandleCommandProcess( wxMenuItem* item, int id, wxWindow* targetWindow )
556 {
557 OSStatus result = eventNotHandledErr ;
558 if (item->IsCheckable())
559 item->Check( !item->IsChecked() ) ;
560
561 if ( SendEvent( id , item->IsCheckable() ? item->IsChecked() : -1 ) )
562 result = noErr ;
563 else
564 {
565 if ( targetWindow != NULL )
566 {
567 wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED , id);
568 event.SetEventObject(targetWindow);
569 event.SetInt(item->IsCheckable() ? item->IsChecked() : -1);
570
571 if ( targetWindow->GetEventHandler()->ProcessEvent(event) )
572 result = noErr ;
573 }
574 }
575 return result;
576 }
577
578 wxInt32 wxMenu::MacHandleCommandUpdateStatus( wxMenuItem* item, int id, wxWindow* targetWindow )
579 {
580 OSStatus result = eventNotHandledErr ;
581 wxUpdateUIEvent event(id);
582 event.SetEventObject( this );
583
584 bool processed = false;
585
586 // Try the menu's event handler
587 {
588 wxEvtHandler *handler = GetEventHandler();
589 if ( handler )
590 processed = handler->ProcessEvent(event);
591 }
592
593 // Try the window the menu was popped up from
594 // (and up through the hierarchy)
595 if ( !processed )
596 {
597 const wxMenuBase *menu = this;
598 while ( menu )
599 {
600 wxWindow *win = menu->GetInvokingWindow();
601 if ( win )
602 {
603 processed = win->GetEventHandler()->ProcessEvent(event);
604 break;
605 }
606
607 menu = menu->GetParent();
608 }
609 }
610
611 if ( !processed && targetWindow != NULL)
612 {
613 processed = targetWindow->GetEventHandler()->ProcessEvent(event);
614 }
615
616 if ( processed )
617 {
618 // if anything changed, update the changed attribute
619 if (event.GetSetText())
620 SetLabel(id, event.GetText());
621 if (event.GetSetChecked())
622 Check(id, event.GetChecked());
623 if (event.GetSetEnabled())
624 Enable(id, event.GetEnabled());
625
626 result = noErr ;
627 }
628 return result;
629 }
630
631 // Menu Bar
632
633 /*
634
635 Mac Implementation note :
636
637 The Mac has only one global menubar, so we attempt to install the currently
638 active menubar from a frame, we currently don't take into account mdi-frames
639 which would ask for menu-merging
640
641 Secondly there is no mac api for changing a menubar that is not the current
642 menubar, so we have to wait for preparing the actual menubar until the
643 wxMenubar is to be used
644
645 We can in subsequent versions use MacInstallMenuBar to provide some sort of
646 auto-merge for MDI in case this will be necessary
647
648 */
649
650 wxMenuBar* wxMenuBar::s_macInstalledMenuBar = NULL ;
651 wxMenuBar* wxMenuBar::s_macCommonMenuBar = NULL ;
652 bool wxMenuBar::s_macAutoWindowMenu = true ;
653 WXHMENU wxMenuBar::s_macWindowMenuHandle = NULL ;
654
655 void wxMenuBar::Init()
656 {
657 m_eventHandler = this;
658 m_menuBarFrame = NULL;
659 m_invokingWindow = (wxWindow*) NULL;
660 }
661
662 wxMenuBar::wxMenuBar()
663 {
664 Init();
665 }
666
667 wxMenuBar::wxMenuBar( long WXUNUSED(style) )
668 {
669 Init();
670 }
671
672 wxMenuBar::wxMenuBar(size_t count, wxMenu *menus[], const wxString titles[], long WXUNUSED(style))
673 {
674 Init();
675
676 m_titles.Alloc(count);
677
678 for ( size_t i = 0; i < count; i++ )
679 {
680 m_menus.Append(menus[i]);
681 m_titles.Add(titles[i]);
682
683 menus[i]->Attach(this);
684 }
685 }
686
687 wxMenuBar::~wxMenuBar()
688 {
689 if (s_macCommonMenuBar == this)
690 s_macCommonMenuBar = NULL;
691
692 if (s_macInstalledMenuBar == this)
693 {
694 ::ClearMenuBar();
695 s_macInstalledMenuBar = NULL;
696 }
697 }
698
699 void wxMenuBar::Refresh(bool WXUNUSED(eraseBackground), const wxRect *WXUNUSED(rect))
700 {
701 wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") );
702
703 DrawMenuBar();
704 }
705
706 void wxMenuBar::MacInstallMenuBar()
707 {
708 if ( s_macInstalledMenuBar == this )
709 return ;
710
711 MenuBarHandle menubar = NULL ;
712
713 #if TARGET_API_MAC_OSX
714 menubar = NewHandleClear( 6 /* sizeof( MenuBarHeader ) */ ) ;
715 #else
716 menubar = NewHandleClear( 12 ) ;
717 (*menubar)[3] = 0x0a ;
718 #endif
719
720 ::SetMenuBar( menubar ) ;
721 DisposeMenuBar( menubar ) ;
722 MenuHandle appleMenu = NULL ;
723
724 verify_noerr( CreateNewMenu( kwxMacAppleMenuId , 0 , &appleMenu ) ) ;
725 verify_noerr( SetMenuTitleWithCFString( appleMenu , CFSTR( "\x14" ) ) );
726
727 // Add About/Preferences separator only on OS X
728 // KH/RN: Separator is always present on 10.3 but not on 10.2
729 // However, the change from 10.2 to 10.3 suggests it is preferred
730 #if TARGET_API_MAC_OSX
731 InsertMenuItemTextWithCFString( appleMenu,
732 CFSTR(""), 0, kMenuItemAttrSeparator, 0);
733 #endif
734 InsertMenuItemTextWithCFString( appleMenu,
735 CFSTR("About..."), 0, 0, 0);
736 MacInsertMenu( appleMenu , 0 ) ;
737
738 // if we have a mac help menu, clean it up before adding new items
739 MenuHandle helpMenuHandle ;
740 MenuItemIndex firstUserHelpMenuItem ;
741
742 if ( UMAGetHelpMenuDontCreate( &helpMenuHandle , &firstUserHelpMenuItem) == noErr )
743 {
744 for ( int i = CountMenuItems( helpMenuHandle ) ; i >= firstUserHelpMenuItem ; --i )
745 DeleteMenuItem( helpMenuHandle , i ) ;
746 }
747 else
748 {
749 helpMenuHandle = NULL ;
750 }
751
752 if ( UMAGetSystemVersion() >= 0x1000 && wxApp::s_macPreferencesMenuItemId)
753 {
754 wxMenuItem *item = FindItem( wxApp::s_macPreferencesMenuItemId , NULL ) ;
755 if ( item == NULL || !(item->IsEnabled()) )
756 DisableMenuCommand( NULL , kHICommandPreferences ) ;
757 else
758 EnableMenuCommand( NULL , kHICommandPreferences ) ;
759 }
760
761 // Unlike preferences which may or may not exist, the Quit item should be always
762 // enabled unless it is added by the application and then disabled, otherwise
763 // a program would be required to add an item with wxID_EXIT in order to get the
764 // Quit menu item to be enabled, which seems a bit burdensome.
765 if ( UMAGetSystemVersion() >= 0x1000 && wxApp::s_macExitMenuItemId)
766 {
767 wxMenuItem *item = FindItem( wxApp::s_macExitMenuItemId , NULL ) ;
768 if ( item != NULL && !(item->IsEnabled()) )
769 DisableMenuCommand( NULL , kHICommandQuit ) ;
770 else
771 EnableMenuCommand( NULL , kHICommandQuit ) ;
772 }
773
774 wxString strippedHelpMenuTitle = wxStripMenuCodes( wxApp::s_macHelpMenuTitleName ) ;
775 wxString strippedTranslatedHelpMenuTitle = wxStripMenuCodes( wxString( _("&Help") ) ) ;
776 wxMenuList::compatibility_iterator menuIter = m_menus.GetFirst();
777 for (size_t i = 0; i < m_menus.GetCount(); i++, menuIter = menuIter->GetNext())
778 {
779 wxMenuItemList::compatibility_iterator node;
780 wxMenuItem *item;
781 wxMenu* menu = menuIter->GetData() , *subMenu = NULL ;
782 wxString strippedMenuTitle = wxStripMenuCodes(m_titles[i]);
783
784 if ( strippedMenuTitle == wxT("?") || strippedMenuTitle == strippedHelpMenuTitle || strippedMenuTitle == strippedTranslatedHelpMenuTitle )
785 {
786 for (node = menu->GetMenuItems().GetFirst(); node; node = node->GetNext())
787 {
788 item = (wxMenuItem *)node->GetData();
789 subMenu = item->GetSubMenu() ;
790 if (subMenu)
791 {
792 // we don't support hierarchical menus in the help menu yet
793 }
794 else
795 {
796 if ( item->GetId() != wxApp::s_macAboutMenuItemId )
797 {
798 // we have found a user help menu and an item other than the about item,
799 // so we can create the mac help menu now, if we haven't created it yet
800 if ( helpMenuHandle == NULL )
801 {
802 if ( UMAGetHelpMenu( &helpMenuHandle , &firstUserHelpMenuItem) != noErr )
803 {
804 helpMenuHandle = NULL ;
805 break ;
806 }
807 }
808 }
809
810 if ( item->IsSeparator() )
811 {
812 if ( helpMenuHandle )
813 AppendMenuItemTextWithCFString( helpMenuHandle,
814 CFSTR(""), kMenuItemAttrSeparator, 0,NULL);
815 }
816 else
817 {
818 wxAcceleratorEntry*
819 entry = wxAcceleratorEntry::Create( item->GetItemLabel() ) ;
820
821 if ( item->GetId() == wxApp::s_macAboutMenuItemId )
822 {
823 // this will be taken care of below
824 }
825 else
826 {
827 if ( helpMenuHandle )
828 {
829 UMAAppendMenuItem(helpMenuHandle, wxStripMenuCodes(item->GetItemLabel()) , wxFont::GetDefaultEncoding(), entry);
830 SetMenuItemCommandID( helpMenuHandle , CountMenuItems(helpMenuHandle) , wxIdToMacCommand ( item->GetId() ) ) ;
831 SetMenuItemRefCon( helpMenuHandle , CountMenuItems(helpMenuHandle) , (URefCon) item ) ;
832 }
833 }
834
835 delete entry ;
836 }
837 }
838 }
839 }
840
841 else if ( ( m_titles[i] == wxT("Window") || m_titles[i] == wxT("&Window") )
842 && GetAutoWindowMenu() )
843 {
844 if ( MacGetWindowMenuHMenu() == NULL )
845 {
846 CreateStandardWindowMenu( 0 , (MenuHandle*) &s_macWindowMenuHandle ) ;
847 }
848
849 MenuRef wm = (MenuRef)MacGetWindowMenuHMenu();
850 if ( wm == NULL )
851 break;
852
853 // get the insertion point in the standard menu
854 MenuItemIndex winListStart;
855 GetIndMenuItemWithCommandID(wm,
856 kHICommandWindowListSeparator, 1, NULL, &winListStart);
857
858 // add a separator so that the standard items and the custom items
859 // aren't mixed together, but only if this is the first run
860 OSStatus err = GetIndMenuItemWithCommandID(wm,
861 'WXWM', 1, NULL, NULL);
862
863 if ( err == menuItemNotFoundErr )
864 {
865 InsertMenuItemTextWithCFString( wm,
866 CFSTR(""), winListStart-1, kMenuItemAttrSeparator, 'WXWM');
867 }
868
869 wxInsertMenuItemsInMenu(menu, wm, winListStart);
870 }
871 else
872 {
873 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , m_titles[i], m_font.GetEncoding() ) ;
874 menu->MacBeforeDisplay(false) ;
875
876 ::InsertMenu(MAC_WXHMENU(_wxMenuAt(m_menus, i)->GetHMenu()), 0);
877 }
878 }
879
880 // take care of the about menu item wherever it is
881 {
882 wxMenu* aboutMenu ;
883 wxMenuItem *aboutMenuItem = FindItem(wxApp::s_macAboutMenuItemId , &aboutMenu) ;
884 if ( aboutMenuItem )
885 {
886 wxAcceleratorEntry*
887 entry = wxAcceleratorEntry::Create( aboutMenuItem->GetItemLabel() ) ;
888 UMASetMenuItemText( GetMenuHandle( kwxMacAppleMenuId ) , 1 , wxStripMenuCodes ( aboutMenuItem->GetItemLabel() ) , wxFont::GetDefaultEncoding() );
889 UMAEnableMenuItem( GetMenuHandle( kwxMacAppleMenuId ) , 1 , true );
890 SetMenuItemCommandID( GetMenuHandle( kwxMacAppleMenuId ) , 1 , kHICommandAbout ) ;
891 SetMenuItemRefCon(GetMenuHandle( kwxMacAppleMenuId ) , 1 , (URefCon)aboutMenuItem ) ;
892 UMASetMenuItemShortcut( GetMenuHandle( kwxMacAppleMenuId ) , 1 , entry ) ;
893 }
894 }
895
896 if ( GetAutoWindowMenu() )
897 {
898 if ( MacGetWindowMenuHMenu() == NULL )
899 CreateStandardWindowMenu( 0 , (MenuHandle*) &s_macWindowMenuHandle ) ;
900
901 InsertMenu( (MenuHandle) MacGetWindowMenuHMenu() , 0 ) ;
902 }
903
904 ::DrawMenuBar() ;
905 s_macInstalledMenuBar = this;
906 }
907
908 void wxMenuBar::EnableTop(size_t pos, bool enable)
909 {
910 wxCHECK_RET( IsAttached(), wxT("doesn't work with unattached menubars") );
911
912 _wxMenuAt(m_menus, pos)->MacEnableMenu( enable ) ;
913 Refresh();
914 }
915
916 bool wxMenuBar::Enable(bool enable)
917 {
918 wxCHECK_MSG( IsAttached(), false, wxT("doesn't work with unattached menubars") );
919
920 size_t i;
921 for (i = 0; i < GetMenuCount(); i++)
922 EnableTop(i, enable);
923
924 return true;
925 }
926
927 void wxMenuBar::SetMenuLabel(size_t pos, const wxString& label)
928 {
929 wxCHECK_RET( pos < GetMenuCount(), wxT("invalid menu index") );
930
931 m_titles[pos] = label;
932
933 if ( !IsAttached() )
934 return;
935
936 _wxMenuAt(m_menus, pos)->SetTitle( label ) ;
937
938 if (wxMenuBar::s_macInstalledMenuBar == this) // are we currently installed ?
939 {
940 ::SetMenuBar( GetMenuBar() ) ;
941 ::InvalMenuBar() ;
942 }
943 }
944
945 wxString wxMenuBar::GetMenuLabel(size_t pos) const
946 {
947 wxCHECK_MSG( pos < GetMenuCount(), wxEmptyString,
948 wxT("invalid menu index in wxMenuBar::GetMenuLabel") );
949
950 return m_titles[pos];
951 }
952
953 int wxMenuBar::FindMenu(const wxString& title)
954 {
955 wxString menuTitle = wxStripMenuCodes(title);
956
957 size_t count = GetMenuCount();
958 for ( size_t i = 0; i < count; i++ )
959 {
960 wxString title = wxStripMenuCodes(m_titles[i]);
961 if ( menuTitle == title )
962 return i;
963 }
964
965 return wxNOT_FOUND;
966 }
967
968 // ---------------------------------------------------------------------------
969 // wxMenuBar construction
970 // ---------------------------------------------------------------------------
971
972 wxMenu *wxMenuBar::Replace(size_t pos, wxMenu *menu, const wxString& title)
973 {
974 wxMenu *menuOld = wxMenuBarBase::Replace(pos, menu, title);
975 if ( !menuOld )
976 return NULL;
977
978 m_titles[pos] = title;
979
980 if ( IsAttached() )
981 {
982 if (s_macInstalledMenuBar == this)
983 {
984 menuOld->MacAfterDisplay( false ) ;
985 ::DeleteMenu( menuOld->MacGetMenuId() /* m_menus[pos]->MacGetMenuId() */ ) ;
986
987 menu->MacBeforeDisplay( false ) ;
988 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , title , m_font.GetEncoding() ) ;
989 if ( pos == m_menus.GetCount() - 1)
990 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , 0 ) ;
991 else
992 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , _wxMenuAt(m_menus, pos + 1)->MacGetMenuId() ) ;
993 }
994
995 Refresh();
996 }
997
998 if (m_invokingWindow)
999 wxMenubarSetInvokingWindow( menu, m_invokingWindow );
1000
1001 return menuOld;
1002 }
1003
1004 bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title)
1005 {
1006 if ( !wxMenuBarBase::Insert(pos, menu, title) )
1007 return false;
1008
1009 m_titles.Insert(title, pos);
1010
1011 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , title , m_font.GetEncoding() ) ;
1012
1013 if ( IsAttached() && s_macInstalledMenuBar == this )
1014 {
1015 if (s_macInstalledMenuBar == this)
1016 {
1017 menu->MacBeforeDisplay( false ) ;
1018
1019 if ( pos == (size_t) -1 || pos + 1 == m_menus.GetCount() )
1020 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , 0 ) ;
1021 else
1022 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , _wxMenuAt(m_menus, pos+1)->MacGetMenuId() ) ;
1023 }
1024
1025 Refresh();
1026 }
1027
1028 if (m_invokingWindow)
1029 wxMenubarSetInvokingWindow( menu, m_invokingWindow );
1030
1031 return true;
1032 }
1033
1034 wxMenu *wxMenuBar::Remove(size_t pos)
1035 {
1036 wxMenu *menu = wxMenuBarBase::Remove(pos);
1037 if ( !menu )
1038 return NULL;
1039
1040 if ( IsAttached() )
1041 {
1042 if (s_macInstalledMenuBar == this)
1043 ::DeleteMenu( menu->MacGetMenuId() /* m_menus[pos]->MacGetMenuId() */ ) ;
1044
1045 Refresh();
1046 }
1047
1048 m_titles.RemoveAt(pos);
1049
1050 return menu;
1051 }
1052
1053 bool wxMenuBar::Append(wxMenu *menu, const wxString& title)
1054 {
1055 WXHMENU submenu = menu ? menu->GetHMenu() : 0;
1056 wxCHECK_MSG( submenu, false, wxT("can't append invalid menu to menubar") );
1057
1058 if ( !wxMenuBarBase::Append(menu, title) )
1059 return false;
1060
1061 m_titles.Add(title);
1062
1063 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , title , m_font.GetEncoding() ) ;
1064
1065 if ( IsAttached() )
1066 {
1067 if (s_macInstalledMenuBar == this)
1068 {
1069 menu->MacBeforeDisplay( false ) ;
1070 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , 0 ) ;
1071 }
1072
1073 Refresh();
1074 }
1075
1076 // m_invokingWindow is set after wxFrame::SetMenuBar(). This call enables
1077 // adding menu later on.
1078 if (m_invokingWindow)
1079 wxMenubarSetInvokingWindow( menu, m_invokingWindow );
1080
1081 return true;
1082 }
1083
1084 static void wxMenubarUnsetInvokingWindow( wxMenu *menu )
1085 {
1086 menu->SetInvokingWindow( (wxWindow*) NULL );
1087 wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst();
1088
1089 while (node)
1090 {
1091 wxMenuItem *menuitem = node->GetData();
1092 if (menuitem->IsSubMenu())
1093 wxMenubarUnsetInvokingWindow( menuitem->GetSubMenu() );
1094
1095 node = node->GetNext();
1096 }
1097 }
1098
1099 static void wxMenubarSetInvokingWindow( wxMenu *menu, wxWindow *win )
1100 {
1101 menu->SetInvokingWindow( win );
1102 wxMenuItem *menuitem;
1103 wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst();
1104
1105 while (node)
1106 {
1107 menuitem = node->GetData();
1108 if (menuitem->IsSubMenu())
1109 wxMenubarSetInvokingWindow( menuitem->GetSubMenu() , win );
1110
1111 node = node->GetNext();
1112 }
1113 }
1114
1115 void wxMenuBar::UnsetInvokingWindow()
1116 {
1117 m_invokingWindow = (wxWindow*) NULL;
1118 wxMenu *menu;
1119 wxMenuList::compatibility_iterator node = m_menus.GetFirst();
1120
1121 while (node)
1122 {
1123 menu = node->GetData();
1124 wxMenubarUnsetInvokingWindow( menu );
1125
1126 node = node->GetNext();
1127 }
1128 }
1129
1130 void wxMenuBar::SetInvokingWindow(wxFrame *frame)
1131 {
1132 m_invokingWindow = frame;
1133 wxMenu *menu;
1134 wxMenuList::compatibility_iterator node = m_menus.GetFirst();
1135
1136 while (node)
1137 {
1138 menu = node->GetData();
1139 wxMenubarSetInvokingWindow( menu, frame );
1140
1141 node = node->GetNext();
1142 }
1143 }
1144
1145 void wxMenuBar::Detach()
1146 {
1147 wxMenuBarBase::Detach() ;
1148 }
1149
1150 void wxMenuBar::Attach(wxFrame *frame)
1151 {
1152 wxMenuBarBase::Attach( frame ) ;
1153 }
1154
1155 // ---------------------------------------------------------------------------
1156 // wxMenuBar searching for menu items
1157 // ---------------------------------------------------------------------------
1158
1159 // Find the itemString in menuString, and return the item id or wxNOT_FOUND
1160 int wxMenuBar::FindMenuItem(const wxString& menuString,
1161 const wxString& itemString) const
1162 {
1163 wxString menuLabel = wxStripMenuCodes(menuString);
1164 size_t count = GetMenuCount();
1165 for ( size_t i = 0; i < count; i++ )
1166 {
1167 wxString title = wxStripMenuCodes(m_titles[i]);
1168 if ( menuLabel == title )
1169 return _wxMenuAt(m_menus, i)->FindItem(itemString);
1170 }
1171
1172 return wxNOT_FOUND;
1173 }
1174
1175 wxMenuItem *wxMenuBar::FindItem(int id, wxMenu **itemMenu) const
1176 {
1177 if ( itemMenu )
1178 *itemMenu = NULL;
1179
1180 wxMenuItem *item = NULL;
1181 size_t count = GetMenuCount();
1182 for ( size_t i = 0; !item && (i < count); i++ )
1183 item = _wxMenuAt(m_menus, i)->FindItem(id, itemMenu);
1184
1185 return item;
1186 }