]> git.saurik.com Git - wxWidgets.git/blob - src/mac/carbon/menu.cpp
<br><br><br> is now handled correctly, e.g. empty lines are inserted (unlike <p>...
[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 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, 4) == 0)
121 {
122 inItemName = inItemName + 5;
123 macShortCut = *inItemName;
124 }
125 else if (strncmp("Alt", inItemName, 3) == 0)
126 {
127 inItemName = inItemName + 4;
128 macModifiers |= kMenuOptionModifier ;
129 macShortCut = *inItemName ;
130 }
131 else if (strncmp("Shift", inItemName, 5) == 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 // helper functions returning the mac menu position for a certain item, note that this is
607 // mac-wise 1 - based, i.e. the first item has index 1 whereas on MSWin it has pos 0
608
609 int wxMenu::MacGetIndexFromId( int id )
610 {
611 size_t pos;
612 wxMenuItemList::Node *node = GetMenuItems().GetFirst();
613 for ( pos = 0; node; pos++ )
614 {
615 if ( node->GetData()->GetId() == id )
616 break;
617
618 node = node->GetNext();
619 }
620
621 if (!node)
622 return 0;
623
624 return pos + 1 ;
625 }
626
627 int wxMenu::MacGetIndexFromItem( wxMenuItem *pItem )
628 {
629 size_t pos;
630 wxMenuItemList::Node *node = GetMenuItems().GetFirst();
631 for ( pos = 0; node; pos++ )
632 {
633 if ( node->GetData() == pItem )
634 break;
635
636 node = node->GetNext();
637 }
638
639 if (!node)
640 return 0;
641
642 return pos + 1 ;
643 }
644
645 void wxMenu::MacEnableMenu( bool bDoEnable )
646 {
647 if ( bDoEnable )
648 UMAEnableMenuItem( m_hMenu , 0 ) ;
649 else
650 UMADisableMenuItem( m_hMenu , 0 ) ;
651
652 ::DrawMenuBar() ;
653 }
654
655 bool wxMenu::MacMenuSelect( wxEvtHandler* handler, long when , int macMenuId, int macMenuItemNum )
656 {
657 int pos;
658 wxNode *node;
659
660 if ( m_macMenuId == macMenuId )
661 {
662 node = GetMenuItems().Nth(macMenuItemNum-1);
663 if (node)
664 {
665 wxMenuItem *pItem = (wxMenuItem*)node->Data();
666
667 wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED, pItem->GetId());
668 event.m_timeStamp = when;
669 event.SetEventObject(handler);
670 event.SetInt( pItem->GetId() );
671 {
672 bool processed = false ;
673
674 #if WXWIN_COMPATIBILITY
675 // Try a callback
676 if (m_callback)
677 {
678 (void) (*(m_callback)) (*this, event);
679 processed = TRUE;
680 }
681 #endif
682 // Try the menu's event handler
683 if ( !processed && handler)
684 {
685 processed = handler->ProcessEvent(event);
686 }
687
688 // Try the window the menu was popped up from (and up
689 // through the hierarchy)
690 if ( !processed && GetInvokingWindow())
691 processed = GetInvokingWindow()->GetEventHandler()->ProcessEvent(event);
692 }
693 return true ;
694 }
695 }
696 else if ( macMenuId == kHMHelpMenuID )
697 {
698 int menuItem = formerHelpMenuItems ;
699 for (pos = 0, node = GetMenuItems().First(); node; node = node->Next(), pos++)
700 {
701 wxMenuItem * pItem = (wxMenuItem *) node->Data() ;
702
703 wxMenu *pSubMenu = pItem->GetSubMenu() ;
704 if ( pSubMenu != NULL )
705 {
706 }
707 else
708 {
709 if ( pItem->GetId() != wxApp::s_macAboutMenuItemId )
710 ++menuItem ;
711
712 if ( menuItem == macMenuItemNum )
713 {
714 wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED, pItem->GetId());
715 event.m_timeStamp = when;
716 event.SetEventObject(handler);
717 event.SetInt( pItem->GetId() );
718 {
719 bool processed = false ;
720 #if WXWIN_COMPATIBILITY
721 // Try a callback
722 if (m_callback)
723 {
724 (void) (*(m_callback)) (*this, event);
725 processed = TRUE;
726 }
727 #endif
728 // Try the menu's event handler
729 if ( !processed && handler)
730 {
731 processed = handler->ProcessEvent(event);
732 }
733
734 // Try the window the menu was popped up from (and up
735 // through the hierarchy)
736 if ( !processed && GetInvokingWindow())
737 processed = GetInvokingWindow()->GetEventHandler()->ProcessEvent(event);
738 }
739 return true ;
740 }
741 }
742 }
743 }
744
745 for (pos = 0, node = GetMenuItems().First(); node; node = node->Next(), pos++)
746 {
747 wxMenuItem * pItem = (wxMenuItem *) node->Data() ;
748
749 wxMenu *pSubMenu = pItem->GetSubMenu() ;
750 if ( pSubMenu != NULL )
751 {
752 if ( pSubMenu->MacMenuSelect( handler , when , macMenuId , macMenuItemNum ) )
753 return true ;
754 }
755 }
756
757 return false ;
758 }
759
760 // Menu Bar
761
762 /*
763
764 Mac Implementation note :
765
766 The Mac has only one global menubar, so we attempt to install the currently
767 active menubar from a frame, we currently don't take into account mdi-frames
768 which would ask for menu-merging
769
770 Secondly there is no mac api for changing a menubar that is not the current
771 menubar, so we have to wait for preparing the actual menubar until the
772 wxMenubar is to be used
773
774 We can in subsequent versions use MacInstallMenuBar to provide some sort of
775 auto-merge for MDI in case this will be necessary
776
777 */
778
779 wxMenuBar* wxMenuBar::s_macInstalledMenuBar = NULL ;
780
781 void wxMenuBar::Init()
782 {
783 m_eventHandler = this;
784 m_menuBarFrame = NULL;
785 }
786
787 wxMenuBar::wxMenuBar()
788 {
789 Init();
790 }
791
792 wxMenuBar::wxMenuBar( long WXUNUSED(style) )
793 {
794 Init();
795 }
796
797
798 wxMenuBar::wxMenuBar(int count, wxMenu *menus[], const wxString titles[])
799 {
800 Init();
801
802 m_titles.Alloc(count);
803
804 for ( int i = 0; i < count; i++ )
805 {
806 m_menus.Append(menus[i]);
807 m_titles.Add(titles[i]);
808
809 menus[i]->Attach(this);
810 }
811 }
812
813 wxMenuBar::~wxMenuBar()
814 {
815 if (s_macInstalledMenuBar == this)
816 {
817 ::ClearMenuBar();
818 s_macInstalledMenuBar = NULL;
819 }
820
821 }
822
823 void wxMenuBar::Refresh()
824 {
825 wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") );
826
827 DrawMenuBar();
828 }
829
830 #if wxUSE_ACCEL
831
832 void wxMenuBar::RebuildAccelTable()
833 {
834 // merge the accelerators of all menus into one accel table
835 size_t nAccelCount = 0;
836 size_t i, count = GetMenuCount();
837 for ( i = 0; i < count; i++ )
838 {
839 nAccelCount += m_menus[i]->GetAccelCount();
840 }
841
842 if ( nAccelCount )
843 {
844 wxAcceleratorEntry *accelEntries = new wxAcceleratorEntry[nAccelCount];
845
846 nAccelCount = 0;
847 for ( i = 0; i < count; i++ )
848 {
849 nAccelCount += m_menus[i]->CopyAccels(&accelEntries[nAccelCount]);
850 }
851
852 m_accelTable = wxAcceleratorTable(nAccelCount, accelEntries);
853
854 delete [] accelEntries;
855 }
856 }
857
858 #endif // wxUSE_ACCEL
859
860
861 void wxMenuBar::MacInstallMenuBar()
862 {
863 Handle menubar = ::GetNewMBar( kwxMacMenuBarResource ) ;
864 wxString message ;
865 wxCHECK_RET( menubar != NULL, "can't read MBAR resource" );
866 ::SetMenuBar( menubar ) ;
867 ::DisposeHandle( menubar ) ;
868
869 MenuHandle menu = ::GetMenuHandle( kwxMacAppleMenuId ) ;
870 ::AppendResMenu(menu, 'DRVR');
871
872 for (int i = 0; i < m_menus.GetCount(); i++)
873 {
874 Str255 label;
875 wxNode *node;
876 wxMenuItem *item;
877 int pos ;
878 wxMenu* menu = m_menus[i] , *subMenu = NULL ;
879
880
881 if( m_titles[i] == "?" || m_titles[i] == wxApp::s_macHelpMenuTitleName )
882 {
883 MenuHandle mh = NULL ;
884 if ( HMGetHelpMenuHandle( &mh ) != noErr )
885 {
886 continue ;
887 }
888 if ( formerHelpMenuItems == 0 )
889 {
890 if( mh )
891 formerHelpMenuItems = CountMenuItems( mh ) ;
892 }
893
894 for (pos = 0 , node = menu->GetMenuItems().First(); node; node = node->Next(), pos++)
895 {
896 item = (wxMenuItem *)node->Data();
897 subMenu = item->GetSubMenu() ;
898 if (subMenu)
899 {
900 // we don't support hierarchical menus in the help menu yet
901 }
902 else
903 {
904 Str255 label ;
905 wxMacBuildMenuString( label , NULL , NULL , item->GetText(), item->GetId() != wxApp::s_macAboutMenuItemId); // no shortcut in about menu
906 if ( label[0] == 0 )
907 {
908 // we cannot add empty menus on mac
909 label[0] = 1 ;
910 label[1] = ' ' ;
911 }
912 if ( item->GetId() == wxApp::s_macAboutMenuItemId )
913 {
914 ::SetMenuItemText( GetMenuHandle( kwxMacAppleMenuId ) , 1 , label );
915 // ::EnableMenuItem( GetMenuHandle( kwxMacAppleMenuId ) , 1 );
916 ::EnableItem( GetMenuHandle( kwxMacAppleMenuId ) , 1 );
917 }
918 else
919 {
920 if ( mh )
921 ::AppendMenu(mh, label );
922 }
923 }
924 }
925 }
926 else
927 {
928 wxMacBuildMenuString( label, NULL , NULL , m_titles[i] , false );
929 UMASetMenuTitle( menu->GetHMenu() , label ) ;
930 for (pos = 0, node = menu->GetMenuItems().First(); node; node = node->Next(), pos++)
931 {
932 item = (wxMenuItem *)node->Data();
933 subMenu = item->GetSubMenu() ;
934 if (subMenu)
935 {
936 ::InsertMenu( subMenu->GetHMenu() , -1 ) ;
937 }
938 }
939 ::InsertMenu(m_menus[i]->GetHMenu(), 0);
940 }
941 }
942 ::DrawMenuBar() ;
943
944 s_macInstalledMenuBar = this;
945 }
946
947 void wxMenuBar::EnableTop(size_t pos, bool enable)
948 {
949 wxCHECK_RET( IsAttached(), wxT("doesn't work with unattached menubars") );
950 m_menus[pos]->MacEnableMenu( enable ) ;
951 Refresh();
952 }
953
954 void wxMenuBar::SetLabelTop(size_t pos, const wxString& label)
955 {
956 wxCHECK_RET( pos < GetMenuCount(), wxT("invalid menu index") );
957
958 m_titles[pos] = label;
959
960 if ( !IsAttached() )
961 {
962 return;
963 }
964
965 m_menus[pos]->SetTitle( label ) ;
966 if (wxMenuBar::s_macInstalledMenuBar == this) // are we currently installed ?
967 {
968 ::SetMenuBar( GetMenuBar() ) ;
969 ::InvalMenuBar() ;
970 }
971 }
972
973 wxString wxMenuBar::GetLabelTop(size_t pos) const
974 {
975 wxCHECK_MSG( pos < GetMenuCount(), wxEmptyString,
976 wxT("invalid menu index in wxMenuBar::GetLabelTop") );
977
978 return m_titles[pos];
979 }
980
981 int wxMenuBar::FindMenu(const wxString& title)
982 {
983 wxString menuTitle = wxStripMenuCodes(title);
984
985 size_t count = GetMenuCount();
986 for ( size_t i = 0; i < count; i++ )
987 {
988 wxString title = wxStripMenuCodes(m_titles[i]);
989 if ( menuTitle == title )
990 return i;
991 }
992
993 return wxNOT_FOUND;
994
995 }
996
997
998 // ---------------------------------------------------------------------------
999 // wxMenuBar construction
1000 // ---------------------------------------------------------------------------
1001
1002 // ---------------------------------------------------------------------------
1003 // wxMenuBar construction
1004 // ---------------------------------------------------------------------------
1005
1006 wxMenu *wxMenuBar::Replace(size_t pos, wxMenu *menu, const wxString& title)
1007 {
1008 wxMenu *menuOld = wxMenuBarBase::Replace(pos, menu, title);
1009 if ( !menuOld )
1010 return FALSE;
1011 m_titles[pos] = title;
1012
1013 if ( IsAttached() )
1014 {
1015 if (s_macInstalledMenuBar == this)
1016 {
1017 ::DeleteMenu( menuOld->MacGetMenuId() /* m_menus[pos]->MacGetMenuId() */ ) ;
1018 {
1019 Str255 label;
1020 wxMacBuildMenuString( label, NULL , NULL , title , false );
1021 UMASetMenuTitle( menu->GetHMenu() , label ) ;
1022 if ( pos == m_menus.GetCount() - 1)
1023 {
1024 ::InsertMenu( menu->GetHMenu() , 0 ) ;
1025 }
1026 else
1027 {
1028 ::InsertMenu( menu->GetHMenu() , m_menus[pos+1]->MacGetMenuId() ) ;
1029 }
1030 }
1031 }
1032
1033
1034 #if wxUSE_ACCEL
1035 if ( menuOld->HasAccels() || menu->HasAccels() )
1036 {
1037 // need to rebuild accell table
1038 RebuildAccelTable();
1039 }
1040 #endif // wxUSE_ACCEL
1041
1042 Refresh();
1043 }
1044
1045 return menuOld;
1046 }
1047
1048 bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title)
1049 {
1050 if ( !wxMenuBarBase::Insert(pos, menu, title) )
1051 return FALSE;
1052
1053 m_titles.Insert(title, pos);
1054
1055 menu->Attach(this);
1056
1057 if ( IsAttached() )
1058 {
1059 if ( pos == (size_t) -1 )
1060 {
1061 ::InsertMenu( menu->GetHMenu() , 0 ) ;
1062 }
1063 else
1064 {
1065 ::InsertMenu( menu->GetHMenu() , m_menus[pos+1]->MacGetMenuId() ) ;
1066 }
1067
1068 #if wxUSE_ACCEL
1069 if ( menu->HasAccels() )
1070 {
1071 // need to rebuild accell table
1072 RebuildAccelTable();
1073 }
1074 #endif // wxUSE_ACCEL
1075
1076 Refresh();
1077 }
1078
1079 return TRUE;
1080 }
1081
1082 void wxMenuBar::MacMenuSelect(wxEvtHandler* handler, long when , int macMenuId, int macMenuItemNum)
1083 {
1084 // first scan fast for direct commands, i.e. menus which have these commands directly in their own list
1085
1086 if ( macMenuId == kwxMacAppleMenuId && macMenuItemNum == 1 )
1087 {
1088 wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED, wxApp::s_macAboutMenuItemId );
1089 event.m_timeStamp = when;
1090 event.SetEventObject(handler);
1091 event.SetInt( wxApp::s_macAboutMenuItemId );
1092 handler->ProcessEvent(event);
1093 }
1094 else
1095 {
1096 for (int i = 0; i < m_menus.GetCount() ; i++)
1097 {
1098 if ( m_menus[i]->MacGetMenuId() == macMenuId ||
1099 ( macMenuId == kHMHelpMenuID && ( m_titles[i] == "?" || m_titles[i] == wxApp::s_macHelpMenuTitleName ) )
1100 )
1101 {
1102 if ( m_menus[i]->MacMenuSelect( handler , when , macMenuId , macMenuItemNum ) )
1103 return ;
1104 else
1105 {
1106 //TODO flag this as an error since it must contain the item
1107 return ;
1108 }
1109 }
1110 }
1111
1112 for (int i = 0; i < m_menus.GetCount(); i++)
1113 {
1114 if ( m_menus[i]->MacMenuSelect( handler , when , macMenuId , macMenuItemNum ) )
1115 {
1116 break ;
1117 }
1118 }
1119 }
1120 }
1121
1122 wxMenu *wxMenuBar::Remove(size_t pos)
1123 {
1124 wxMenu *menu = wxMenuBarBase::Remove(pos);
1125 if ( !menu )
1126 return NULL;
1127
1128 if ( IsAttached() )
1129 {
1130 if (s_macInstalledMenuBar == this)
1131 {
1132 ::DeleteMenu( menu->MacGetMenuId() /* m_menus[pos]->MacGetMenuId() */ ) ;
1133 }
1134
1135 menu->Detach();
1136
1137 #if wxUSE_ACCEL
1138 if ( menu->HasAccels() )
1139 {
1140 // need to rebuild accell table
1141 RebuildAccelTable();
1142 }
1143 #endif // wxUSE_ACCEL
1144
1145 Refresh();
1146 }
1147
1148 m_titles.Remove(pos);
1149
1150 return menu;
1151 }
1152
1153 bool wxMenuBar::Append(wxMenu *menu, const wxString& title)
1154 {
1155 WXHMENU submenu = menu ? menu->GetHMenu() : 0;
1156 wxCHECK_MSG( submenu, FALSE, wxT("can't append invalid menu to menubar") );
1157
1158 if ( !wxMenuBarBase::Append(menu, title) )
1159 return FALSE;
1160
1161 menu->Attach(this);
1162
1163 m_titles.Add(title);
1164
1165 if ( IsAttached() )
1166 {
1167 if (s_macInstalledMenuBar == this)
1168 {
1169 ::InsertMenu( menu->GetHMenu() , 0 ) ;
1170 }
1171
1172 #if wxUSE_ACCEL
1173 if ( menu->HasAccels() )
1174 {
1175 // need to rebuild accell table
1176 RebuildAccelTable();
1177 }
1178 #endif // wxUSE_ACCEL
1179
1180 Refresh();
1181 }
1182
1183 return TRUE;
1184 }
1185
1186 // ---------------------------------------------------------------------------
1187 // wxMenuBar searching for menu items
1188 // ---------------------------------------------------------------------------
1189
1190 // Find the itemString in menuString, and return the item id or wxNOT_FOUND
1191 int wxMenuBar::FindMenuItem(const wxString& menuString,
1192 const wxString& itemString) const
1193 {
1194 wxString menuLabel = wxStripMenuCodes(menuString);
1195 size_t count = GetMenuCount();
1196 for ( size_t i = 0; i < count; i++ )
1197 {
1198 wxString title = wxStripMenuCodes(m_titles[i]);
1199 if ( menuString == title )
1200 return m_menus[i]->FindItem(itemString);
1201 }
1202
1203 return wxNOT_FOUND;
1204 }
1205
1206 wxMenuItem *wxMenuBar::FindItem(int id, wxMenu **itemMenu) const
1207 {
1208 if ( itemMenu )
1209 *itemMenu = NULL;
1210
1211 wxMenuItem *item = NULL;
1212 size_t count = GetMenuCount();
1213 for ( size_t i = 0; !item && (i < count); i++ )
1214 {
1215 item = m_menus[i]->FindItem(id, itemMenu);
1216 }
1217
1218 return item;
1219 }
1220
1221