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