]> git.saurik.com Git - wxWidgets.git/blob - src/mac/carbon/menu.cpp
35589ca6e1f35b93b5fd8b6ba9b37f604adef325
[wxWidgets.git] / src / mac / carbon / menu.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: menu.cpp
3 // Purpose: wxMenu, wxMenuBar, wxMenuItem
4 // Author: AUTHOR
5 // Modified by:
6 // Created: ??/??/98
7 // RCS-ID: $Id$
8 // Copyright: (c) AUTHOR
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12
13 // ============================================================================
14 // headers & declarations
15 // ============================================================================
16
17 // wxWindows headers
18 // -----------------
19
20 #ifdef __GNUG__
21 #pragma implementation "menu.h"
22 #pragma implementation "menuitem.h"
23 #endif
24
25 #include "wx/menu.h"
26 #include "wx/menuitem.h"
27 #include "wx/log.h"
28 #include "wx/utils.h"
29
30 #include "wx/mac/uma.h"
31
32 // other standard headers
33 // ----------------------
34 #include <string.h>
35
36 #if !USE_SHARED_LIBRARY
37 IMPLEMENT_DYNAMIC_CLASS(wxMenu, wxEvtHandler)
38 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar, wxEvtHandler)
39 #endif
40
41 // the (popup) menu title has this special id
42 static const int idMenuTitle = -2;
43 static int formerHelpMenuItems = 0 ;
44
45 const short kwxMacMenuBarResource = 1 ;
46 const short kwxMacAppleMenuId = 1 ;
47
48 // ============================================================================
49 // implementation
50 // ============================================================================
51
52 //
53 // Helper Functions to get Mac Menus the way they should be ;-)
54 //
55
56 void wxMacCtoPString(const char* theCString, Str255 thePString);
57
58 // remove inappropriate characters, if useShortcuts is false, the ampersand will not auto-generate a mac menu-shortcut
59
60 static void wxMacBuildMenuString(StringPtr outMacItemText, char *outMacShortcutChar , short *outMacModifiers , const char *inItemName , bool useShortcuts )
61 {
62 char *p = (char *) &outMacItemText[1] ;
63 short macModifiers = 0 ;
64 char macShortCut = 0 ;
65
66 if ( useShortcuts && !wxApp::s_macSupportPCMenuShortcuts )
67 useShortcuts = false ;
68
69 // we have problems with a leading hypen - it will be taken as a separator
70
71 while ( *inItemName == '-' )
72 inItemName++ ;
73
74 while( *inItemName )
75 {
76 switch ( *inItemName )
77 {
78 // special characters for macintosh menus -> use some replacement
79 case ';' :
80 *p++ = ',' ;
81 break ;
82 case '^' :
83 *p++ = ' ' ;
84 break ;
85 case '!' :
86 *p++ = ' ' ;
87 break ;
88 case '<' :
89 *p++ = ' ' ;
90 break ;
91 case '/' :
92 *p++ = '|' ;
93 break ;
94 case '(' :
95 *p++ = '[' ;
96 break ;
97 case ')' :
98 *p++ = ']' ;
99 break ;
100 // shortcuts
101 case '&' :
102 {
103 ++inItemName ;
104 if ( *inItemName )
105 {
106 *p++ = *inItemName ;
107 if ( useShortcuts )
108 macShortCut = *inItemName ;
109 }
110 else
111 --inItemName ;
112 }
113 break ;
114 // win-like accelerators
115 case '\t' :
116 {
117 ++inItemName ;
118 while( *inItemName )
119 {
120 if (strncmp("Ctrl+", inItemName, 5) == 0)
121 {
122 inItemName = inItemName + 5;
123 macShortCut = *inItemName;
124 }
125 else if (strncmp("Alt+", inItemName, 4) == 0)
126 {
127 inItemName = inItemName + 4;
128 macModifiers |= kMenuOptionModifier ;
129 macShortCut = *inItemName ;
130 }
131 else if (strncmp("Shift+", inItemName, 6) == 0)
132 {
133 inItemName = inItemName + 6;
134 macModifiers |= kMenuShiftModifier ;
135 macShortCut = *inItemName ;
136 }
137 else if (strncmp("F", inItemName, 1) == 0)
138 {
139 inItemName += strlen( inItemName ) ;
140 // no function keys at the moment
141 // macModifiers |= kMenuShiftModifier ;
142 // macShortCut = *inItemName ;
143 }
144 else
145 {
146 break ;
147 }
148 }
149
150 if ( *inItemName == 0 )
151 --inItemName ;
152
153 }
154 break ;
155 default :
156 *p++ = *inItemName ;
157 }
158 ++inItemName ;
159 }
160
161 outMacItemText[0] = (p - (char *)outMacItemText) - 1;
162 if ( outMacShortcutChar )
163 *outMacShortcutChar = macShortCut ;
164 if ( outMacModifiers )
165 *outMacModifiers = macModifiers ;
166 if ( macShortCut )
167 {
168 int pos = outMacItemText[0] ;
169 outMacItemText[++pos] = '/';
170 outMacItemText[++pos] = toupper( macShortCut );
171 outMacItemText[0] = pos ;
172 }
173 }
174
175 // Menus
176
177 // Construct a menu with optional title (then use append)
178
179 short wxMenu::s_macNextMenuId = 2 ;
180
181 void wxMenu::Init()
182 {
183 m_doBreak = FALSE;
184
185 // create the menu
186 Str255 label;
187 wxMacBuildMenuString( label, NULL , NULL , m_title , false );
188 m_macMenuId = s_macNextMenuId++;
189 wxCHECK_RET( s_macNextMenuId < 236 , "menu ids > 235 cannot be used for submenus on mac" );
190 m_hMenu = ::NewMenu(m_macMenuId, label);
191
192 if ( !m_hMenu )
193 {
194 wxLogLastError("CreatePopupMenu");
195 }
196
197 // if we have a title, insert it in the beginning of the menu
198 if ( !!m_title )
199 {
200 Append(idMenuTitle, m_title) ;
201 AppendSeparator() ;
202 }
203 }
204
205 wxMenu::~wxMenu()
206 {
207 if (m_hMenu)
208 ::DisposeMenu(m_hMenu);
209
210 #if wxUSE_ACCEL
211 // delete accels
212 WX_CLEAR_ARRAY(m_accels);
213 #endif // wxUSE_ACCEL
214 }
215
216 void wxMenu::Break()
217 {
218 // not available on the mac platform
219 }
220
221 #if wxUSE_ACCEL
222
223 int wxMenu::FindAccel(int id) const
224 {
225 size_t n, count = m_accels.GetCount();
226 for ( n = 0; n < count; n++ )
227 {
228 if ( m_accels[n]->m_command == id )
229 return n;
230 }
231
232 return wxNOT_FOUND;
233 }
234
235 void wxMenu::UpdateAccel(wxMenuItem *item)
236 {
237 // find the (new) accel for this item
238 wxAcceleratorEntry *accel = wxGetAccelFromString(item->GetText());
239 if ( accel )
240 accel->m_command = item->GetId();
241
242 // find the old one
243 int n = FindAccel(item->GetId());
244 if ( n == wxNOT_FOUND )
245 {
246 // no old, add new if any
247 if ( accel )
248 m_accels.Add(accel);
249 else
250 return; // skipping RebuildAccelTable() below
251 }
252 else
253 {
254 // replace old with new or just remove the old one if no new
255 delete m_accels[n];
256 if ( accel )
257 m_accels[n] = accel;
258 else
259 m_accels.Remove(n);
260 }
261
262 if ( IsAttached() )
263 {
264 m_menuBar->RebuildAccelTable();
265 }
266 }
267
268 #endif // wxUSE_ACCEL
269
270 // function appends a new item or submenu to the menu
271 // append a new item or submenu to the menu
272 bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos)
273 {
274 wxASSERT_MSG( pItem != NULL, "can't append NULL item to the menu" );
275 #if wxUSE_ACCEL
276 UpdateAccel(pItem);
277 #endif // wxUSE_ACCEL
278
279 if ( pItem->IsSeparator() )
280 {
281 if ( pos == (size_t)-1 )
282 {
283 MacAppendMenu(m_hMenu, "\p-");
284 }
285 else
286 {
287 MacInsertMenuItem(m_hMenu, "\p-" , pos);
288 }
289 }
290 else
291 {
292 wxMenu *pSubMenu = pItem->GetSubMenu() ;
293 if ( pSubMenu != NULL )
294 {
295 Str255 label;
296 wxASSERT_MSG( pSubMenu->m_hMenu != NULL , "invalid submenu added");
297 pSubMenu->m_menuParent = this ;
298 wxMacBuildMenuString( label , NULL , NULL , pItem->GetText() ,false);
299
300 // hardcoded adding of the submenu combination for mac
301
302 int theEnd = label[0] + 1;
303 if (theEnd > 251)
304 theEnd = 251; // mac allows only 255 characters
305 label[theEnd++] = '/';
306 label[theEnd++] = hMenuCmd;
307 label[theEnd++] = '!';
308 label[theEnd++] = pSubMenu->m_macMenuId;
309 label[theEnd] = 0x00;
310 label[0] = theEnd;
311
312 if (wxMenuBar::MacGetInstalledMenuBar() == m_menuBar)
313 {
314 ::InsertMenu( pSubMenu->m_hMenu , -1 ) ;
315 }
316
317 if ( pos == (size_t)-1 )
318 {
319 MacAppendMenu(m_hMenu, label);
320 }
321 else
322 {
323 MacInsertMenuItem(m_hMenu, label , pos);
324 }
325 }
326 else
327 {
328 Str255 label ;
329 wxMacBuildMenuString( label , NULL , NULL , pItem->GetText(), pItem->GetId() == wxApp::s_macAboutMenuItemId);
330 if ( label[0] == 0 )
331 {
332 // we cannot add empty menus on mac
333 label[0] = 1 ;
334 label[1] = ' ' ;
335 }
336 if ( pos == (size_t)-1 )
337 {
338 MacAppendMenu(m_hMenu, label);
339 }
340 else
341 {
342 MacInsertMenuItem(m_hMenu, label , pos);
343 }
344 if ( pItem->GetId() == idMenuTitle )
345 {
346 if ( pos == (size_t)-1 )
347 {
348 UMADisableMenuItem( m_hMenu , CountMItems( m_hMenu ) ) ;
349 }
350 else
351 {
352 UMADisableMenuItem( m_hMenu , pos + 1 ) ;
353 }
354 }
355 }
356 }
357 // if we're already attached to the menubar, we must update it
358 if ( IsAttached() )
359 {
360 m_menuBar->Refresh();
361 }
362 return TRUE ;
363 }
364
365 bool wxMenu::DoAppend(wxMenuItem *item)
366 {
367 return wxMenuBase::DoAppend(item) && DoInsertOrAppend(item);
368 }
369
370 bool wxMenu::DoInsert(size_t pos, wxMenuItem *item)
371 {
372 return wxMenuBase::DoInsert(pos, item) && DoInsertOrAppend(item, pos);
373 }
374
375 wxMenuItem *wxMenu::DoRemove(wxMenuItem *item)
376 {
377 // we need to find the items position in the child list
378 size_t pos;
379 wxMenuItemList::Node *node = GetMenuItems().GetFirst();
380 for ( pos = 0; node; pos++ )
381 {
382 if ( node->GetData() == item )
383 break;
384
385 node = node->GetNext();
386 }
387
388 // DoRemove() (unlike Remove) can only be called for existing item!
389 wxCHECK_MSG( node, NULL, wxT("bug in wxMenu::Remove logic") );
390
391 #if wxUSE_ACCEL
392 // remove the corresponding accel from the accel table
393 int n = FindAccel(item->GetId());
394 if ( n != wxNOT_FOUND )
395 {
396 delete m_accels[n];
397
398 m_accels.Remove(n);
399 }
400 //else: this item doesn't have an accel, nothing to do
401 #endif // wxUSE_ACCEL
402
403 ::DeleteMenuItem( m_hMenu , pos + 1);
404
405 if ( IsAttached() )
406 {
407 // otherwise, the chane won't be visible
408 m_menuBar->Refresh();
409 }
410
411 // and from internal data structures
412 return wxMenuBase::DoRemove(item);
413 }
414
415 // ---------------------------------------------------------------------------
416 // accelerator helpers
417 // ---------------------------------------------------------------------------
418
419 #if wxUSE_ACCEL
420
421 // create the wxAcceleratorEntries for our accels and put them into provided
422 // array - return the number of accels we have
423 size_t wxMenu::CopyAccels(wxAcceleratorEntry *accels) const
424 {
425 size_t count = GetAccelCount();
426 for ( size_t n = 0; n < count; n++ )
427 {
428 *accels++ = *m_accels[n];
429 }
430
431 return count;
432 }
433
434 #endif // wxUSE_ACCEL
435
436 void wxMenu::SetTitle(const wxString& label)
437 {
438 Str255 title ;
439 m_title = label ;
440 wxMacBuildMenuString( title, NULL , NULL , label , false );
441 UMASetMenuTitle( m_hMenu , title ) ;
442 }
443
444 /*
445
446 void wxMenu::SetLabel(int id, const wxString& label)
447 {
448 Str255 maclabel ;
449 int index ;
450 wxMenuItem *item = FindItemForId(id) ;
451 if (item==NULL)
452 return;
453
454 index = MacGetIndexFromItem( item ) ;
455 if (index < 1)
456 return;
457
458 if (item->GetSubMenu()==NULL)
459 {
460 wxMacBuildMenuString( maclabel , NULL , NULL , label , false );
461 ::SetMenuItemText( m_hMenu , index , maclabel ) ;
462 }
463 else
464 {
465 wxMacBuildMenuString( maclabel , NULL , NULL , label , false );
466 ::SetMenuItemText( m_hMenu , index , maclabel ) ;
467 }
468 item->SetName(label);
469 }
470
471 wxString wxMenu::GetLabel(int Id) const
472 {
473 wxMenuItem *pItem = FindItemForId(Id) ;
474 return pItem->GetName() ;
475 }
476
477 // Finds the item id matching the given string, -1 if not found.
478 int wxMenu::FindItem (const wxString& itemString) const
479 {
480 char buf1[200];
481 char buf2[200];
482 wxStripMenuCodes ((char *)(const char *)itemString, buf1);
483
484 for (wxNode * node = m_menuItems.First (); node; node = node->Next ())
485 {
486 wxMenuItem *item = (wxMenuItem *) node->Data ();
487 if (item->GetSubMenu())
488 {
489 int ans = item->GetSubMenu()->FindItem(itemString);
490 if (ans > -1)
491 return ans;
492 }
493 if ( !item->IsSeparator() )
494 {
495 wxStripMenuCodes((char *)item->GetName().c_str(), buf2);
496 if (strcmp(buf1, buf2) == 0)
497 return item->GetId();
498 }
499 }
500
501 return -1;
502 }
503
504 wxMenuItem *wxMenu::FindItemForId(int itemId, wxMenu ** itemMenu) const
505 {
506 if (itemMenu)
507 *itemMenu = NULL;
508 for (wxNode * node = m_menuItems.First (); node; node = node->Next ())
509 {
510 wxMenuItem *item = (wxMenuItem *) node->Data ();
511
512 if (item->GetId() == itemId)
513 {
514 if (itemMenu)
515 *itemMenu = (wxMenu *) this;
516 return item;
517 }
518
519 if (item->GetSubMenu())
520 {
521 wxMenuItem *ans = item->GetSubMenu()->FindItemForId (itemId, itemMenu);
522 if (ans)
523 return ans;
524 }
525 }
526
527 if (itemMenu)
528 *itemMenu = NULL;
529 return NULL;
530 }
531
532 void wxMenu::SetHelpString(int itemId, const wxString& helpString)
533 {
534 wxMenuItem *item = FindItemForId (itemId);
535 if (item)
536 item->SetHelp(helpString);
537 }
538
539 wxString wxMenu::GetHelpString (int itemId) const
540 {
541 wxMenuItem *item = FindItemForId (itemId);
542 wxString str("");
543 return (item == NULL) ? str : item->GetHelp();
544 }
545 */
546
547 bool wxMenu::ProcessCommand(wxCommandEvent & event)
548 {
549 bool processed = FALSE;
550
551 #if WXWIN_COMPATIBILITY
552 // Try a callback
553 if (m_callback)
554 {
555 (void)(*(m_callback))(*this, event);
556 processed = TRUE;
557 }
558 #endif WXWIN_COMPATIBILITY
559
560 // Try the menu's event handler
561 if ( !processed && GetEventHandler())
562 {
563 processed = GetEventHandler()->ProcessEvent(event);
564 }
565
566 // Try the window the menu was popped up from (and up through the
567 // hierarchy)
568 wxWindow *win = GetInvokingWindow();
569 if ( !processed && win )
570 processed = win->GetEventHandler()->ProcessEvent(event);
571
572 return processed;
573 }
574
575
576 // ---------------------------------------------------------------------------
577 // other
578 // ---------------------------------------------------------------------------
579
580 void wxMenu::Attach(wxMenuBar *menubar)
581 {
582 // menu can be in at most one menubar because otherwise they would both
583 // delete the menu pointer
584 wxASSERT_MSG( !m_menuBar, wxT("menu belongs to 2 menubars, expect a crash") );
585
586 m_menuBar = menubar;
587 }
588
589 void wxMenu::Detach()
590 {
591 wxASSERT_MSG( m_menuBar, wxT("can't detach menu if it's not attached") );
592
593 m_menuBar = NULL;
594 }
595
596 wxWindow *wxMenu::GetWindow() const
597 {
598 if ( m_invokingWindow != NULL )
599 return m_invokingWindow;
600 else if ( m_menuBar != NULL)
601 return m_menuBar->GetFrame();
602
603 return NULL;
604 }
605 /*
606 bool wxWindow::PopupMenu(wxMenu *menu, int x, int y)
607 {
608 menu->SetInvokingWindow(this);
609 ClientToScreen( &x , &y ) ;
610
611 ::InsertMenu( menu->m_hMenu , -1 ) ;
612 long menuResult = ::PopUpMenuSelect(menu->m_hMenu ,y,x, 0) ;
613 menu->MacMenuSelect( this , TickCount() , HiWord(menuResult) , LoWord(menuResult) ) ;
614 ::DeleteMenu( menu->m_macMenuId ) ;
615 menu->SetInvokingWindow(NULL);
616
617 return TRUE;
618 }
619 */
620 // helper functions returning the mac menu position for a certain item, note that this is
621 // mac-wise 1 - based, i.e. the first item has index 1 whereas on MSWin it has pos 0
622
623 int wxMenu::MacGetIndexFromId( int id )
624 {
625 size_t pos;
626 wxMenuItemList::Node *node = GetMenuItems().GetFirst();
627 for ( pos = 0; node; pos++ )
628 {
629 if ( node->GetData()->GetId() == id )
630 break;
631
632 node = node->GetNext();
633 }
634
635 if (!node)
636 return 0;
637
638 return pos + 1 ;
639 }
640
641 int wxMenu::MacGetIndexFromItem( wxMenuItem *pItem )
642 {
643 size_t pos;
644 wxMenuItemList::Node *node = GetMenuItems().GetFirst();
645 for ( pos = 0; node; pos++ )
646 {
647 if ( node->GetData() == pItem )
648 break;
649
650 node = node->GetNext();
651 }
652
653 if (!node)
654 return 0;
655
656 return pos + 1 ;
657 }
658
659 void wxMenu::MacEnableMenu( bool bDoEnable )
660 {
661 if ( bDoEnable )
662 UMAEnableMenuItem( m_hMenu , 0 ) ;
663 else
664 UMADisableMenuItem( m_hMenu , 0 ) ;
665
666 ::DrawMenuBar() ;
667 }
668
669 bool wxMenu::MacMenuSelect( wxEvtHandler* handler, long when , int macMenuId, int macMenuItemNum )
670 {
671 int pos;
672 wxNode *node;
673
674 if ( m_macMenuId == macMenuId )
675 {
676 node = GetMenuItems().Nth(macMenuItemNum-1);
677 if (node)
678 {
679 wxMenuItem *pItem = (wxMenuItem*)node->Data();
680
681 wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED, pItem->GetId());
682 event.m_timeStamp = when;
683 event.SetEventObject(handler);
684 event.SetInt( pItem->GetId() );
685 ProcessCommand( event ) ;
686 return true ;
687 }
688 }
689 else if ( macMenuId == kHMHelpMenuID )
690 {
691 int menuItem = formerHelpMenuItems ;
692 for (pos = 0, node = GetMenuItems().First(); node; node = node->Next(), pos++)
693 {
694 wxMenuItem * pItem = (wxMenuItem *) node->Data() ;
695
696 wxMenu *pSubMenu = pItem->GetSubMenu() ;
697 if ( pSubMenu != NULL )
698 {
699 }
700 else
701 {
702 if ( pItem->GetId() != wxApp::s_macAboutMenuItemId )
703 ++menuItem ;
704
705 if ( menuItem == macMenuItemNum )
706 {
707 wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED, pItem->GetId());
708 event.m_timeStamp = when;
709 event.SetEventObject(handler);
710 event.SetInt( pItem->GetId() );
711 ProcessCommand( event ) ;
712 return true ;
713 }
714 }
715 }
716 }
717
718 for (pos = 0, node = GetMenuItems().First(); node; node = node->Next(), pos++)
719 {
720 wxMenuItem * pItem = (wxMenuItem *) node->Data() ;
721
722 wxMenu *pSubMenu = pItem->GetSubMenu() ;
723 if ( pSubMenu != NULL )
724 {
725 if ( pSubMenu->MacMenuSelect( handler , when , macMenuId , macMenuItemNum ) )
726 return true ;
727 }
728 }
729
730 return false ;
731 }
732
733 // Menu Bar
734
735 /*
736
737 Mac Implementation note :
738
739 The Mac has only one global menubar, so we attempt to install the currently
740 active menubar from a frame, we currently don't take into account mdi-frames
741 which would ask for menu-merging
742
743 Secondly there is no mac api for changing a menubar that is not the current
744 menubar, so we have to wait for preparing the actual menubar until the
745 wxMenubar is to be used
746
747 We can in subsequent versions use MacInstallMenuBar to provide some sort of
748 auto-merge for MDI in case this will be necessary
749
750 */
751
752 wxMenuBar* wxMenuBar::s_macInstalledMenuBar = NULL ;
753
754 void wxMenuBar::Init()
755 {
756 m_eventHandler = this;
757 m_menuBarFrame = NULL;
758 }
759
760
761 wxMenuBar::wxMenuBar(int count, wxMenu *menus[], const wxString titles[])
762 {
763 Init();
764
765 m_titles.Alloc(count);
766
767 for ( int i = 0; i < count; i++ )
768 {
769 m_menus.Append(menus[i]);
770 m_titles.Add(titles[i]);
771
772 menus[i]->Attach(this);
773 }
774 }
775
776 wxMenuBar::~wxMenuBar()
777 {
778 if (s_macInstalledMenuBar == this)
779 {
780 ::ClearMenuBar();
781 s_macInstalledMenuBar = NULL;
782 }
783
784 }
785
786 void wxMenuBar::Refresh()
787 {
788 wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") );
789
790 DrawMenuBar();
791 }
792
793 void wxMenuBar::MacInstallMenuBar()
794 {
795 Handle menubar = ::GetNewMBar( kwxMacMenuBarResource ) ;
796 wxString message ;
797 wxCHECK_RET( menubar != NULL, "can't read MBAR resource" );
798 ::SetMenuBar( menubar ) ;
799 ::DisposeHandle( menubar ) ;
800
801 MenuHandle menu = ::GetMenuHandle( kwxMacAppleMenuId ) ;
802 ::AppendResMenu(menu, 'DRVR');
803
804 for (int i = 0; i < m_menus.GetCount(); i++)
805 {
806 Str255 label;
807 wxNode *node;
808 wxMenuItem *item;
809 int pos ;
810 wxMenu* menu = m_menus[i] , *subMenu = NULL ;
811
812
813 if( m_titles[i] == "?" || m_titles[i] == wxApp::s_macHelpMenuTitleName )
814 {
815 MenuHandle mh = NULL ;
816 if ( HMGetHelpMenuHandle( &mh ) != noErr )
817 {
818 continue ;
819 }
820 if ( formerHelpMenuItems == 0 )
821 {
822 if( mh )
823 formerHelpMenuItems = CountMenuItems( mh ) ;
824 }
825
826 for (pos = 0 , node = menu->GetMenuItems().First(); node; node = node->Next(), pos++)
827 {
828 item = (wxMenuItem *)node->Data();
829 subMenu = item->GetSubMenu() ;
830 if (subMenu)
831 {
832 // we don't support hierarchical menus in the help menu yet
833 }
834 else
835 {
836 Str255 label ;
837 wxMacBuildMenuString( label , NULL , NULL , item->GetText(), item->GetId() != wxApp::s_macAboutMenuItemId); // no shortcut in about menu
838 if ( label[0] == 0 )
839 {
840 // we cannot add empty menus on mac
841 label[0] = 1 ;
842 label[1] = ' ' ;
843 }
844 if ( item->GetId() == wxApp::s_macAboutMenuItemId )
845 {
846 ::SetMenuItemText( GetMenuHandle( kwxMacAppleMenuId ) , 1 , label );
847 // ::EnableMenuItem( GetMenuHandle( kwxMacAppleMenuId ) , 1 );
848 ::EnableItem( GetMenuHandle( kwxMacAppleMenuId ) , 1 );
849 }
850 else
851 {
852 if ( mh )
853 ::AppendMenu(mh, label );
854 }
855 }
856 }
857 }
858 else
859 {
860 wxMacBuildMenuString( label, NULL , NULL , m_titles[i] , false );
861 UMASetMenuTitle( menu->GetHMenu() , label ) ;
862 for (pos = 0, node = menu->GetMenuItems().First(); node; node = node->Next(), pos++)
863 {
864 item = (wxMenuItem *)node->Data();
865 subMenu = item->GetSubMenu() ;
866 if (subMenu)
867 {
868 ::InsertMenu( subMenu->GetHMenu() , -1 ) ;
869 }
870 }
871 ::InsertMenu(m_menus[i]->GetHMenu(), 0);
872 }
873 }
874 ::DrawMenuBar() ;
875
876 s_macInstalledMenuBar = this;
877 }
878
879 void wxMenuBar::EnableTop(size_t pos, bool enable)
880 {
881 wxCHECK_RET( IsAttached(), wxT("doesn't work with unattached menubars") );
882 m_menus[pos]->MacEnableMenu( enable ) ;
883 Refresh();
884 }
885
886 void wxMenuBar::SetLabelTop(size_t pos, const wxString& label)
887 {
888 wxCHECK_RET( pos < GetMenuCount(), wxT("invalid menu index") );
889
890 m_titles[pos] = label;
891
892 if ( !IsAttached() )
893 {
894 return;
895 }
896
897 m_menus[pos]->SetTitle( label ) ;
898 if (wxMenuBar::s_macInstalledMenuBar == this) // are we currently installed ?
899 {
900 ::SetMenuBar( GetMenuBar() ) ;
901 ::InvalMenuBar() ;
902 }
903 }
904
905 wxString wxMenuBar::GetLabelTop(size_t pos) const
906 {
907 wxCHECK_MSG( pos < GetMenuCount(), wxEmptyString,
908 wxT("invalid menu index in wxMenuBar::GetLabelTop") );
909
910 return m_titles[pos];
911 }
912
913 int wxMenuBar::FindMenu(const wxString& title)
914 {
915 wxString menuTitle = wxStripMenuCodes(title);
916
917 size_t count = GetMenuCount();
918 for ( size_t i = 0; i < count; i++ )
919 {
920 wxString title = wxStripMenuCodes(m_titles[i]);
921 if ( menuTitle == title )
922 return i;
923 }
924
925 return wxNOT_FOUND;
926
927 }
928
929
930 // ---------------------------------------------------------------------------
931 // wxMenuBar construction
932 // ---------------------------------------------------------------------------
933
934 // ---------------------------------------------------------------------------
935 // wxMenuBar construction
936 // ---------------------------------------------------------------------------
937
938 wxMenu *wxMenuBar::Replace(size_t pos, wxMenu *menu, const wxString& title)
939 {
940 wxMenu *menuOld = wxMenuBarBase::Replace(pos, menu, title);
941 if ( !menuOld )
942 return FALSE;
943 m_titles[pos] = title;
944
945 if ( IsAttached() )
946 {
947 if (s_macInstalledMenuBar == this)
948 {
949 ::DeleteMenu( menuOld->MacGetMenuId() /* m_menus[pos]->MacGetMenuId() */ ) ;
950 {
951 Str255 label;
952 wxMacBuildMenuString( label, NULL , NULL , title , false );
953 UMASetMenuTitle( menu->GetHMenu() , label ) ;
954 if ( pos == m_menus.GetCount() - 1)
955 {
956 ::InsertMenu( menu->GetHMenu() , 0 ) ;
957 }
958 else
959 {
960 ::InsertMenu( menu->GetHMenu() , m_menus[pos+1]->MacGetMenuId() ) ;
961 }
962 }
963 }
964
965
966 #if wxUSE_ACCEL
967 if ( menuOld->HasAccels() || menu->HasAccels() )
968 {
969 // need to rebuild accell table
970 RebuildAccelTable();
971 }
972 #endif // wxUSE_ACCEL
973
974 Refresh();
975 }
976
977 return menuOld;
978 }
979
980 bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title)
981 {
982 if ( !wxMenuBarBase::Insert(pos, menu, title) )
983 return FALSE;
984
985 m_titles.Insert(title, pos);
986
987 menu->Attach(this);
988
989 if ( IsAttached() )
990 {
991 if ( pos == (size_t) -1 )
992 {
993 ::InsertMenu( menu->GetHMenu() , 0 ) ;
994 }
995 else
996 {
997 ::InsertMenu( menu->GetHMenu() , m_menus[pos+1]->MacGetMenuId() ) ;
998 }
999
1000 #if wxUSE_ACCEL
1001 if ( menu->HasAccels() )
1002 {
1003 // need to rebuild accell table
1004 RebuildAccelTable();
1005 }
1006 #endif // wxUSE_ACCEL
1007
1008 Refresh();
1009 }
1010
1011 return TRUE;
1012 }
1013
1014 void wxMenuBar::MacMenuSelect(wxEvtHandler* handler, long when , int macMenuId, int macMenuItemNum)
1015 {
1016 // first scan fast for direct commands, i.e. menus which have these commands directly in their own list
1017
1018 if ( macMenuId == kwxMacAppleMenuId && macMenuItemNum == 1 )
1019 {
1020 wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED, wxApp::s_macAboutMenuItemId );
1021 event.m_timeStamp = when;
1022 event.SetEventObject(handler);
1023 event.SetInt( wxApp::s_macAboutMenuItemId );
1024 handler->ProcessEvent(event);
1025 }
1026 else
1027 {
1028 for (int i = 0; i < m_menus.GetCount() ; i++)
1029 {
1030 if ( m_menus[i]->MacGetMenuId() == macMenuId ||
1031 ( macMenuId == kHMHelpMenuID && ( m_titles[i] == "?" || m_titles[i] == wxApp::s_macHelpMenuTitleName ) )
1032 )
1033 {
1034 if ( m_menus[i]->MacMenuSelect( handler , when , macMenuId , macMenuItemNum ) )
1035 return ;
1036 else
1037 {
1038 //TODO flag this as an error since it must contain the item
1039 return ;
1040 }
1041 }
1042 }
1043
1044 for (int i = 0; i < m_menus.GetCount(); i++)
1045 {
1046 if ( m_menus[i]->MacMenuSelect( handler , when , macMenuId , macMenuItemNum ) )
1047 {
1048 break ;
1049 }
1050 }
1051 }
1052 }
1053