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