]> git.saurik.com Git - wxWidgets.git/blob - src/mac/carbon/menu.cpp
Added extra width for controls to avoid edge being clipped
[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->GetText() ) ;
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->GetText()) , 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->GetText()), wxFont::GetDefaultEncoding(), pSubMenu->m_macMenuId);
231 else
232 UMAInsertSubMenuItem(MAC_WXHMENU(m_hMenu), wxStripMenuCodes(pItem->GetText()), 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 #if TARGET_CARBON
469 // what we do here is to hide the special items which are
470 // shown in the application menu anyhow -- it doesn't make
471 // sense to show them in their normal place as well
472 if ( item->GetId() == wxApp::s_macAboutMenuItemId ||
473 ( UMAGetSystemVersion() >= 0x1000 && (
474 item->GetId() == wxApp::s_macPreferencesMenuItemId ||
475 item->GetId() == wxApp::s_macExitMenuItemId ) ) )
476
477 {
478 ChangeMenuItemAttributes( MAC_WXHMENU( GetHMenu() ),
479 pos + 1, kMenuItemAttrHidden, 0 );
480
481 // also check for a separator which was used just to
482 // separate this item from the others, so don't leave
483 // separator at the menu start or end nor 2 consecutive
484 // separators
485 wxMenuItemList::compatibility_iterator nextNode = node->GetNext();
486 wxMenuItem *next = nextNode ? nextNode->GetData() : NULL;
487
488 size_t posSeptoHide;
489 if ( !previousItem && next && next->IsSeparator() )
490 {
491 // next (i.e. second as we must be first) item is
492 // the separator to hide
493 wxASSERT_MSG( pos == 0, _T("should be the menu start") );
494 posSeptoHide = 2;
495 }
496 else if ( GetMenuItems().GetCount() == pos + 1 &&
497 previousItem != NULL &&
498 previousItem->IsSeparator() )
499 {
500 // prev item is a trailing separator we want to hide
501 posSeptoHide = pos;
502 }
503 else if ( previousItem && previousItem->IsSeparator() &&
504 next && next->IsSeparator() )
505 {
506 // two consecutive separators, this is one too many
507 posSeptoHide = pos;
508 }
509 else // no separators to hide
510 {
511 posSeptoHide = 0;
512 }
513
514 if ( posSeptoHide )
515 {
516 // hide the separator as well
517 ChangeMenuItemAttributes( MAC_WXHMENU( GetHMenu() ),
518 posSeptoHide,
519 kMenuItemAttrHidden,
520 0 );
521 }
522 }
523 #endif // TARGET_CARBON
524 }
525
526 previousItem = item ;
527 }
528
529 if ( isSubMenu )
530 ::InsertMenu(MAC_WXHMENU( GetHMenu()), -1);
531 }
532
533 // undo all changes from the MacBeforeDisplay call
534 void wxMenu::MacAfterDisplay( bool isSubMenu )
535 {
536 if ( isSubMenu )
537 ::DeleteMenu(MacGetMenuId());
538
539 wxMenuItemList::compatibility_iterator node;
540 wxMenuItem *item;
541
542 for (node = GetMenuItems().GetFirst(); node; node = node->GetNext())
543 {
544 item = (wxMenuItem *)node->GetData();
545 wxMenu* subMenu = item->GetSubMenu() ;
546 if (subMenu)
547 {
548 subMenu->MacAfterDisplay( true ) ;
549 }
550 else
551 {
552 // no need to undo hidings
553 }
554 }
555 }
556
557 // Menu Bar
558
559 /*
560
561 Mac Implementation note :
562
563 The Mac has only one global menubar, so we attempt to install the currently
564 active menubar from a frame, we currently don't take into account mdi-frames
565 which would ask for menu-merging
566
567 Secondly there is no mac api for changing a menubar that is not the current
568 menubar, so we have to wait for preparing the actual menubar until the
569 wxMenubar is to be used
570
571 We can in subsequent versions use MacInstallMenuBar to provide some sort of
572 auto-merge for MDI in case this will be necessary
573
574 */
575
576 wxMenuBar* wxMenuBar::s_macInstalledMenuBar = NULL ;
577 wxMenuBar* wxMenuBar::s_macCommonMenuBar = NULL ;
578 bool wxMenuBar::s_macAutoWindowMenu = true ;
579 WXHMENU wxMenuBar::s_macWindowMenuHandle = NULL ;
580
581 void wxMenuBar::Init()
582 {
583 m_eventHandler = this;
584 m_menuBarFrame = NULL;
585 m_invokingWindow = (wxWindow*) NULL;
586 }
587
588 wxMenuBar::wxMenuBar()
589 {
590 Init();
591 }
592
593 wxMenuBar::wxMenuBar( long WXUNUSED(style) )
594 {
595 Init();
596 }
597
598 wxMenuBar::wxMenuBar(size_t count, wxMenu *menus[], const wxString titles[], long WXUNUSED(style))
599 {
600 Init();
601
602 m_titles.Alloc(count);
603
604 for ( size_t i = 0; i < count; i++ )
605 {
606 m_menus.Append(menus[i]);
607 m_titles.Add(titles[i]);
608
609 menus[i]->Attach(this);
610 }
611 }
612
613 wxMenuBar::~wxMenuBar()
614 {
615 if (s_macCommonMenuBar == this)
616 s_macCommonMenuBar = NULL;
617
618 if (s_macInstalledMenuBar == this)
619 {
620 ::ClearMenuBar();
621 s_macInstalledMenuBar = NULL;
622 }
623 }
624
625 void wxMenuBar::Refresh(bool WXUNUSED(eraseBackground), const wxRect *WXUNUSED(rect))
626 {
627 wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") );
628
629 DrawMenuBar();
630 }
631
632 void wxMenuBar::MacInstallMenuBar()
633 {
634 if ( s_macInstalledMenuBar == this )
635 return ;
636
637 MenuBarHandle menubar = NULL ;
638
639 #if TARGET_API_MAC_OSX
640 menubar = NewHandleClear( 6 /* sizeof( MenuBarHeader ) */ ) ;
641 #else
642 menubar = NewHandleClear( 12 ) ;
643 (*menubar)[3] = 0x0a ;
644 #endif
645
646 ::SetMenuBar( menubar ) ;
647 DisposeMenuBar( menubar ) ;
648 MenuHandle appleMenu = NULL ;
649
650 verify_noerr( CreateNewMenu( kwxMacAppleMenuId , 0 , &appleMenu ) ) ;
651 verify_noerr( SetMenuTitleWithCFString( appleMenu , CFSTR( "\x14" ) ) );
652
653 // Add About/Preferences separator only on OS X
654 // KH/RN: Separator is always present on 10.3 but not on 10.2
655 // However, the change from 10.2 to 10.3 suggests it is preferred
656 #if TARGET_API_MAC_OSX
657 InsertMenuItemTextWithCFString( appleMenu,
658 CFSTR(""), 0, kMenuItemAttrSeparator, 0);
659 #endif
660 InsertMenuItemTextWithCFString( appleMenu,
661 CFSTR("About..."), 0, 0, 0);
662 MacInsertMenu( appleMenu , 0 ) ;
663
664 // if we have a mac help menu, clean it up before adding new items
665 MenuHandle helpMenuHandle ;
666 MenuItemIndex firstUserHelpMenuItem ;
667
668 if ( UMAGetHelpMenuDontCreate( &helpMenuHandle , &firstUserHelpMenuItem) == noErr )
669 {
670 for ( int i = CountMenuItems( helpMenuHandle ) ; i >= firstUserHelpMenuItem ; --i )
671 DeleteMenuItem( helpMenuHandle , i ) ;
672 }
673 else
674 {
675 helpMenuHandle = NULL ;
676 }
677
678 #if TARGET_CARBON
679 if ( UMAGetSystemVersion() >= 0x1000 && wxApp::s_macPreferencesMenuItemId)
680 {
681 wxMenuItem *item = FindItem( wxApp::s_macPreferencesMenuItemId , NULL ) ;
682 if ( item == NULL || !(item->IsEnabled()) )
683 DisableMenuCommand( NULL , kHICommandPreferences ) ;
684 else
685 EnableMenuCommand( NULL , kHICommandPreferences ) ;
686 }
687
688 // Unlike preferences which may or may not exist, the Quit item should be always
689 // enabled unless it is added by the application and then disabled, otherwise
690 // a program would be required to add an item with wxID_EXIT in order to get the
691 // Quit menu item to be enabled, which seems a bit burdensome.
692 if ( UMAGetSystemVersion() >= 0x1000 && wxApp::s_macExitMenuItemId)
693 {
694 wxMenuItem *item = FindItem( wxApp::s_macExitMenuItemId , NULL ) ;
695 if ( item != NULL && !(item->IsEnabled()) )
696 DisableMenuCommand( NULL , kHICommandQuit ) ;
697 else
698 EnableMenuCommand( NULL , kHICommandQuit ) ;
699 }
700 #endif
701
702 wxString strippedHelpMenuTitle = wxStripMenuCodes( wxApp::s_macHelpMenuTitleName ) ;
703 wxString strippedTranslatedHelpMenuTitle = wxStripMenuCodes( wxString( _("&Help") ) ) ;
704 wxMenuList::compatibility_iterator menuIter = m_menus.GetFirst();
705 for (size_t i = 0; i < m_menus.GetCount(); i++, menuIter = menuIter->GetNext())
706 {
707 wxMenuItemList::compatibility_iterator node;
708 wxMenuItem *item;
709 wxMenu* menu = menuIter->GetData() , *subMenu = NULL ;
710 wxString strippedMenuTitle = wxStripMenuCodes(m_titles[i]);
711
712 if ( strippedMenuTitle == wxT("?") || strippedMenuTitle == strippedHelpMenuTitle || strippedMenuTitle == strippedTranslatedHelpMenuTitle )
713 {
714 for (node = menu->GetMenuItems().GetFirst(); node; node = node->GetNext())
715 {
716 item = (wxMenuItem *)node->GetData();
717 subMenu = item->GetSubMenu() ;
718 if (subMenu)
719 {
720 // we don't support hierarchical menus in the help menu yet
721 }
722 else
723 {
724 if ( item->GetId() != wxApp::s_macAboutMenuItemId )
725 {
726 // we have found a user help menu and an item other than the about item,
727 // so we can create the mac help menu now, if we haven't created it yet
728 if ( helpMenuHandle == NULL )
729 {
730 if ( UMAGetHelpMenu( &helpMenuHandle , &firstUserHelpMenuItem) != noErr )
731 {
732 helpMenuHandle = NULL ;
733 break ;
734 }
735 }
736 }
737
738 if ( item->IsSeparator() )
739 {
740 if ( helpMenuHandle )
741 AppendMenuItemTextWithCFString( helpMenuHandle,
742 CFSTR(""), kMenuItemAttrSeparator, 0,NULL);
743 }
744 else
745 {
746 wxAcceleratorEntry*
747 entry = wxAcceleratorEntry::Create( item->GetText() ) ;
748
749 if ( item->GetId() == wxApp::s_macAboutMenuItemId )
750 {
751 // this will be taken care of below
752 }
753 else
754 {
755 if ( helpMenuHandle )
756 {
757 UMAAppendMenuItem(helpMenuHandle, wxStripMenuCodes(item->GetText()) , wxFont::GetDefaultEncoding(), entry);
758 SetMenuItemCommandID( helpMenuHandle , CountMenuItems(helpMenuHandle) , wxIdToMacCommand ( item->GetId() ) ) ;
759 SetMenuItemRefCon( helpMenuHandle , CountMenuItems(helpMenuHandle) , (URefCon) item ) ;
760 }
761 }
762
763 delete entry ;
764 }
765 }
766 }
767 }
768
769 else if ( ( m_titles[i] == wxT("Window") || m_titles[i] == wxT("&Window") )
770 && GetAutoWindowMenu() )
771 {
772 if ( MacGetWindowMenuHMenu() == NULL )
773 {
774 CreateStandardWindowMenu( 0 , (MenuHandle*) &s_macWindowMenuHandle ) ;
775 }
776
777 MenuRef wm = (MenuRef)MacGetWindowMenuHMenu();
778 if ( wm == NULL )
779 break;
780
781 // get the insertion point in the standard menu
782 MenuItemIndex winListStart;
783 GetIndMenuItemWithCommandID(wm,
784 kHICommandWindowListSeparator, 1, NULL, &winListStart);
785
786 // add a separator so that the standard items and the custom items
787 // aren't mixed together, but only if this is the first run
788 OSStatus err = GetIndMenuItemWithCommandID(wm,
789 'WXWM', 1, NULL, NULL);
790
791 if ( err == menuItemNotFoundErr )
792 {
793 InsertMenuItemTextWithCFString( wm,
794 CFSTR(""), winListStart-1, kMenuItemAttrSeparator, 'WXWM');
795 }
796
797 wxInsertMenuItemsInMenu(menu, wm, winListStart);
798 }
799 else
800 {
801 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , m_titles[i], m_font.GetEncoding() ) ;
802 menu->MacBeforeDisplay(false) ;
803
804 ::InsertMenu(MAC_WXHMENU(_wxMenuAt(m_menus, i)->GetHMenu()), 0);
805 }
806 }
807
808 // take care of the about menu item wherever it is
809 {
810 wxMenu* aboutMenu ;
811 wxMenuItem *aboutMenuItem = FindItem(wxApp::s_macAboutMenuItemId , &aboutMenu) ;
812 if ( aboutMenuItem )
813 {
814 wxAcceleratorEntry*
815 entry = wxAcceleratorEntry::Create( aboutMenuItem->GetText() ) ;
816 UMASetMenuItemText( GetMenuHandle( kwxMacAppleMenuId ) , 1 , wxStripMenuCodes ( aboutMenuItem->GetText() ) , wxFont::GetDefaultEncoding() );
817 UMAEnableMenuItem( GetMenuHandle( kwxMacAppleMenuId ) , 1 , true );
818 SetMenuItemCommandID( GetMenuHandle( kwxMacAppleMenuId ) , 1 , kHICommandAbout ) ;
819 SetMenuItemRefCon(GetMenuHandle( kwxMacAppleMenuId ) , 1 , (URefCon)aboutMenuItem ) ;
820 UMASetMenuItemShortcut( GetMenuHandle( kwxMacAppleMenuId ) , 1 , entry ) ;
821 }
822 }
823
824 if ( GetAutoWindowMenu() )
825 {
826 if ( MacGetWindowMenuHMenu() == NULL )
827 CreateStandardWindowMenu( 0 , (MenuHandle*) &s_macWindowMenuHandle ) ;
828
829 InsertMenu( (MenuHandle) MacGetWindowMenuHMenu() , 0 ) ;
830 }
831
832 ::DrawMenuBar() ;
833 s_macInstalledMenuBar = this;
834 }
835
836 void wxMenuBar::EnableTop(size_t pos, bool enable)
837 {
838 wxCHECK_RET( IsAttached(), wxT("doesn't work with unattached menubars") );
839
840 _wxMenuAt(m_menus, pos)->MacEnableMenu( enable ) ;
841 Refresh();
842 }
843
844 bool wxMenuBar::Enable(bool enable)
845 {
846 wxCHECK_MSG( IsAttached(), false, wxT("doesn't work with unattached menubars") );
847
848 size_t i;
849 for (i = 0; i < GetMenuCount(); i++)
850 EnableTop(i, enable);
851
852 return true;
853 }
854
855 void wxMenuBar::SetLabelTop(size_t pos, const wxString& label)
856 {
857 wxCHECK_RET( pos < GetMenuCount(), wxT("invalid menu index") );
858
859 m_titles[pos] = label;
860
861 if ( !IsAttached() )
862 return;
863
864 _wxMenuAt(m_menus, pos)->SetTitle( label ) ;
865
866 if (wxMenuBar::s_macInstalledMenuBar == this) // are we currently installed ?
867 {
868 ::SetMenuBar( GetMenuBar() ) ;
869 ::InvalMenuBar() ;
870 }
871 }
872
873 wxString wxMenuBar::GetLabelTop(size_t pos) const
874 {
875 wxCHECK_MSG( pos < GetMenuCount(), wxEmptyString,
876 wxT("invalid menu index in wxMenuBar::GetLabelTop") );
877
878 return m_titles[pos];
879 }
880
881 int wxMenuBar::FindMenu(const wxString& title)
882 {
883 wxString menuTitle = wxStripMenuCodes(title);
884
885 size_t count = GetMenuCount();
886 for ( size_t i = 0; i < count; i++ )
887 {
888 wxString title = wxStripMenuCodes(m_titles[i]);
889 if ( menuTitle == title )
890 return i;
891 }
892
893 return wxNOT_FOUND;
894 }
895
896 // ---------------------------------------------------------------------------
897 // wxMenuBar construction
898 // ---------------------------------------------------------------------------
899
900 wxMenu *wxMenuBar::Replace(size_t pos, wxMenu *menu, const wxString& title)
901 {
902 wxMenu *menuOld = wxMenuBarBase::Replace(pos, menu, title);
903 if ( !menuOld )
904 return NULL;
905
906 m_titles[pos] = title;
907
908 if ( IsAttached() )
909 {
910 if (s_macInstalledMenuBar == this)
911 {
912 menuOld->MacAfterDisplay( false ) ;
913 ::DeleteMenu( menuOld->MacGetMenuId() /* m_menus[pos]->MacGetMenuId() */ ) ;
914
915 menu->MacBeforeDisplay( false ) ;
916 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , title , m_font.GetEncoding() ) ;
917 if ( pos == m_menus.GetCount() - 1)
918 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , 0 ) ;
919 else
920 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , _wxMenuAt(m_menus, pos + 1)->MacGetMenuId() ) ;
921 }
922
923 Refresh();
924 }
925
926 if (m_invokingWindow)
927 wxMenubarSetInvokingWindow( menu, m_invokingWindow );
928
929 return menuOld;
930 }
931
932 bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title)
933 {
934 if ( !wxMenuBarBase::Insert(pos, menu, title) )
935 return false;
936
937 m_titles.Insert(title, pos);
938
939 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , title , m_font.GetEncoding() ) ;
940
941 if ( IsAttached() && s_macInstalledMenuBar == this )
942 {
943 if (s_macInstalledMenuBar == this)
944 {
945 menu->MacBeforeDisplay( false ) ;
946
947 if ( pos == (size_t) -1 || pos + 1 == m_menus.GetCount() )
948 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , 0 ) ;
949 else
950 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , _wxMenuAt(m_menus, pos+1)->MacGetMenuId() ) ;
951 }
952
953 Refresh();
954 }
955
956 if (m_invokingWindow)
957 wxMenubarSetInvokingWindow( menu, m_invokingWindow );
958
959 return true;
960 }
961
962 wxMenu *wxMenuBar::Remove(size_t pos)
963 {
964 wxMenu *menu = wxMenuBarBase::Remove(pos);
965 if ( !menu )
966 return NULL;
967
968 if ( IsAttached() )
969 {
970 if (s_macInstalledMenuBar == this)
971 ::DeleteMenu( menu->MacGetMenuId() /* m_menus[pos]->MacGetMenuId() */ ) ;
972
973 Refresh();
974 }
975
976 m_titles.RemoveAt(pos);
977
978 return menu;
979 }
980
981 bool wxMenuBar::Append(wxMenu *menu, const wxString& title)
982 {
983 WXHMENU submenu = menu ? menu->GetHMenu() : 0;
984 wxCHECK_MSG( submenu, false, wxT("can't append invalid menu to menubar") );
985
986 if ( !wxMenuBarBase::Append(menu, title) )
987 return false;
988
989 m_titles.Add(title);
990
991 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , title , m_font.GetEncoding() ) ;
992
993 if ( IsAttached() )
994 {
995 if (s_macInstalledMenuBar == this)
996 {
997 menu->MacBeforeDisplay( false ) ;
998 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , 0 ) ;
999 }
1000
1001 Refresh();
1002 }
1003
1004 // m_invokingWindow is set after wxFrame::SetMenuBar(). This call enables
1005 // adding menu later on.
1006 if (m_invokingWindow)
1007 wxMenubarSetInvokingWindow( menu, m_invokingWindow );
1008
1009 return true;
1010 }
1011
1012 static void wxMenubarUnsetInvokingWindow( wxMenu *menu )
1013 {
1014 menu->SetInvokingWindow( (wxWindow*) NULL );
1015 wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst();
1016
1017 while (node)
1018 {
1019 wxMenuItem *menuitem = node->GetData();
1020 if (menuitem->IsSubMenu())
1021 wxMenubarUnsetInvokingWindow( menuitem->GetSubMenu() );
1022
1023 node = node->GetNext();
1024 }
1025 }
1026
1027 static void wxMenubarSetInvokingWindow( wxMenu *menu, wxWindow *win )
1028 {
1029 menu->SetInvokingWindow( win );
1030 wxMenuItem *menuitem;
1031 wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst();
1032
1033 while (node)
1034 {
1035 menuitem = node->GetData();
1036 if (menuitem->IsSubMenu())
1037 wxMenubarSetInvokingWindow( menuitem->GetSubMenu() , win );
1038
1039 node = node->GetNext();
1040 }
1041 }
1042
1043 void wxMenuBar::UnsetInvokingWindow()
1044 {
1045 m_invokingWindow = (wxWindow*) NULL;
1046 wxMenu *menu;
1047 wxMenuList::compatibility_iterator node = m_menus.GetFirst();
1048
1049 while (node)
1050 {
1051 menu = node->GetData();
1052 wxMenubarUnsetInvokingWindow( menu );
1053
1054 node = node->GetNext();
1055 }
1056 }
1057
1058 void wxMenuBar::SetInvokingWindow(wxFrame *frame)
1059 {
1060 m_invokingWindow = frame;
1061 wxMenu *menu;
1062 wxMenuList::compatibility_iterator node = m_menus.GetFirst();
1063
1064 while (node)
1065 {
1066 menu = node->GetData();
1067 wxMenubarSetInvokingWindow( menu, frame );
1068
1069 node = node->GetNext();
1070 }
1071 }
1072
1073 void wxMenuBar::Detach()
1074 {
1075 wxMenuBarBase::Detach() ;
1076 }
1077
1078 void wxMenuBar::Attach(wxFrame *frame)
1079 {
1080 wxMenuBarBase::Attach( frame ) ;
1081 }
1082
1083 // ---------------------------------------------------------------------------
1084 // wxMenuBar searching for menu items
1085 // ---------------------------------------------------------------------------
1086
1087 // Find the itemString in menuString, and return the item id or wxNOT_FOUND
1088 int wxMenuBar::FindMenuItem(const wxString& menuString,
1089 const wxString& itemString) const
1090 {
1091 wxString menuLabel = wxStripMenuCodes(menuString);
1092 size_t count = GetMenuCount();
1093 for ( size_t i = 0; i < count; i++ )
1094 {
1095 wxString title = wxStripMenuCodes(m_titles[i]);
1096 if ( menuLabel == title )
1097 return _wxMenuAt(m_menus, i)->FindItem(itemString);
1098 }
1099
1100 return wxNOT_FOUND;
1101 }
1102
1103 wxMenuItem *wxMenuBar::FindItem(int id, wxMenu **itemMenu) const
1104 {
1105 if ( itemMenu )
1106 *itemMenu = NULL;
1107
1108 wxMenuItem *item = NULL;
1109 size_t count = GetMenuCount();
1110 for ( size_t i = 0; !item && (i < count); i++ )
1111 item = _wxMenuAt(m_menus, i)->FindItem(id, itemMenu);
1112
1113 return item;
1114 }