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