]> git.saurik.com Git - wxWidgets.git/blame - src/mac/carbon/menu.cpp
VTK wrapper of vtkRenderWindow for wxPython. Tested on MSW so far.
[wxWidgets.git] / src / mac / carbon / menu.cpp
CommitLineData
e9576ca5
SC
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
519cb848
SC
30#include "wx/mac/uma.h"
31
e9576ca5
SC
32// other standard headers
33// ----------------------
34#include <string.h>
35
36#if !USE_SHARED_LIBRARY
37IMPLEMENT_DYNAMIC_CLASS(wxMenu, wxEvtHandler)
38IMPLEMENT_DYNAMIC_CLASS(wxMenuBar, wxEvtHandler)
39#endif
40
519cb848
SC
41// the (popup) menu title has this special id
42static const int idMenuTitle = -2;
43static int formerHelpMenuItems = 0 ;
44
45const short kwxMacMenuBarResource = 1 ;
46const short kwxMacAppleMenuId = 1 ;
47
e9576ca5
SC
48// ============================================================================
49// implementation
50// ============================================================================
51
519cb848
SC
52//
53// Helper Functions to get Mac Menus the way they should be ;-)
54//
55
56void 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
51abe921 60void wxMacBuildMenuString(StringPtr outMacItemText, char *outMacShortcutChar , short *outMacModifiers , const char *inItemName , bool useShortcuts )
519cb848
SC
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 {
51abe921 120 if (strncmp("Ctrl", inItemName, 4) == 0)
519cb848
SC
121 {
122 inItemName = inItemName + 5;
123 macShortCut = *inItemName;
124 }
51abe921 125 else if (strncmp("Alt", inItemName, 3) == 0)
519cb848
SC
126 {
127 inItemName = inItemName + 4;
128 macModifiers |= kMenuOptionModifier ;
129 macShortCut = *inItemName ;
130 }
51abe921 131 else if (strncmp("Shift", inItemName, 5) == 0)
519cb848
SC
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
e9576ca5
SC
175// Menus
176
177// Construct a menu with optional title (then use append)
519cb848
SC
178
179short wxMenu::s_macNextMenuId = 2 ;
180
e7549107 181void wxMenu::Init()
e9576ca5 182{
e7549107
SC
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 )
e9576ca5 199 {
519cb848 200 Append(idMenuTitle, m_title) ;
e9576ca5
SC
201 AppendSeparator() ;
202 }
e7549107 203}
e9576ca5 204
e7549107
SC
205wxMenu::~wxMenu()
206{
207 if (m_hMenu)
208 ::DisposeMenu(m_hMenu);
e9576ca5 209
e7549107
SC
210#if wxUSE_ACCEL
211 // delete accels
212 WX_CLEAR_ARRAY(m_accels);
213#endif // wxUSE_ACCEL
e9576ca5
SC
214}
215
e7549107 216void wxMenu::Break()
e9576ca5 217{
e7549107
SC
218 // not available on the mac platform
219}
e9576ca5 220
e7549107 221#if wxUSE_ACCEL
e9576ca5 222
e7549107
SC
223int 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;
e9576ca5 230 }
e7549107
SC
231
232 return wxNOT_FOUND;
e9576ca5
SC
233}
234
e7549107 235void wxMenu::UpdateAccel(wxMenuItem *item)
e9576ca5 236{
e7549107
SC
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 }
e9576ca5
SC
266}
267
e7549107
SC
268#endif // wxUSE_ACCEL
269
e9576ca5 270// function appends a new item or submenu to the menu
e7549107
SC
271// append a new item or submenu to the menu
272bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos)
e9576ca5 273{
e7549107
SC
274 wxASSERT_MSG( pItem != NULL, "can't append NULL item to the menu" );
275#if wxUSE_ACCEL
276 UpdateAccel(pItem);
277#endif // wxUSE_ACCEL
e9576ca5 278
519cb848
SC
279 if ( pItem->IsSeparator() )
280 {
e7549107
SC
281 if ( pos == (size_t)-1 )
282 {
283 MacAppendMenu(m_hMenu, "\p-");
284 }
285 else
286 {
287 MacInsertMenuItem(m_hMenu, "\p-" , pos);
288 }
519cb848
SC
289 }
290 else
291 {
292 wxMenu *pSubMenu = pItem->GetSubMenu() ;
293 if ( pSubMenu != NULL )
294 {
295 Str255 label;
e7549107
SC
296 wxASSERT_MSG( pSubMenu->m_hMenu != NULL , "invalid submenu added");
297 pSubMenu->m_menuParent = this ;
298 wxMacBuildMenuString( label , NULL , NULL , pItem->GetText() ,false);
519cb848
SC
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
e7549107 312 if (wxMenuBar::MacGetInstalledMenuBar() == m_menuBar)
519cb848 313 {
e7549107 314 ::InsertMenu( pSubMenu->m_hMenu , -1 ) ;
519cb848
SC
315 }
316
e7549107
SC
317 if ( pos == (size_t)-1 )
318 {
319 MacAppendMenu(m_hMenu, label);
320 }
321 else
322 {
323 MacInsertMenuItem(m_hMenu, label , pos);
324 }
519cb848
SC
325 }
326 else
327 {
328 Str255 label ;
e7549107 329 wxMacBuildMenuString( label , NULL , NULL , pItem->GetText(), pItem->GetId() == wxApp::s_macAboutMenuItemId);
519cb848
SC
330 if ( label[0] == 0 )
331 {
332 // we cannot add empty menus on mac
333 label[0] = 1 ;
334 label[1] = ' ' ;
335 }
e7549107
SC
336 if ( pos == (size_t)-1 )
337 {
338 MacAppendMenu(m_hMenu, label);
339 }
340 else
341 {
342 MacInsertMenuItem(m_hMenu, label , pos);
343 }
519cb848
SC
344 if ( pItem->GetId() == idMenuTitle )
345 {
e7549107
SC
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 }
519cb848
SC
354 }
355 }
356 }
e7549107
SC
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 ;
e9576ca5
SC
363}
364
e7549107 365bool wxMenu::DoAppend(wxMenuItem *item)
e9576ca5 366{
e7549107 367 return wxMenuBase::DoAppend(item) && DoInsertOrAppend(item);
e9576ca5
SC
368}
369
e7549107 370bool wxMenu::DoInsert(size_t pos, wxMenuItem *item)
e9576ca5 371{
e7549107 372 return wxMenuBase::DoInsert(pos, item) && DoInsertOrAppend(item, pos);
e9576ca5
SC
373}
374
e7549107 375wxMenuItem *wxMenu::DoRemove(wxMenuItem *item)
e9576ca5 376{
e7549107
SC
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;
e9576ca5 384
e7549107 385 node = node->GetNext();
e9576ca5
SC
386 }
387
e7549107
SC
388 // DoRemove() (unlike Remove) can only be called for existing item!
389 wxCHECK_MSG( node, NULL, wxT("bug in wxMenu::Remove logic") );
519cb848 390
e7549107
SC
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];
519cb848 397
e7549107
SC
398 m_accels.Remove(n);
399 }
400 //else: this item doesn't have an accel, nothing to do
401#endif // wxUSE_ACCEL
e9576ca5 402
e7549107 403 ::DeleteMenuItem( m_hMenu , pos + 1);
e9576ca5 404
e7549107
SC
405 if ( IsAttached() )
406 {
407 // otherwise, the chane won't be visible
408 m_menuBar->Refresh();
409 }
e9576ca5 410
e7549107
SC
411 // and from internal data structures
412 return wxMenuBase::DoRemove(item);
e9576ca5
SC
413}
414
e7549107
SC
415// ---------------------------------------------------------------------------
416// accelerator helpers
417// ---------------------------------------------------------------------------
e9576ca5 418
e7549107 419#if wxUSE_ACCEL
e9576ca5 420
e7549107
SC
421// create the wxAcceleratorEntries for our accels and put them into provided
422// array - return the number of accels we have
423size_t wxMenu::CopyAccels(wxAcceleratorEntry *accels) const
e9576ca5 424{
e7549107
SC
425 size_t count = GetAccelCount();
426 for ( size_t n = 0; n < count; n++ )
427 {
428 *accels++ = *m_accels[n];
429 }
e9576ca5 430
e7549107 431 return count;
e9576ca5
SC
432}
433
e7549107 434#endif // wxUSE_ACCEL
e9576ca5
SC
435
436void wxMenu::SetTitle(const wxString& label)
437{
519cb848 438 Str255 title ;
e9576ca5 439 m_title = label ;
519cb848 440 wxMacBuildMenuString( title, NULL , NULL , label , false );
e7549107 441 UMASetMenuTitle( m_hMenu , title ) ;
e9576ca5
SC
442}
443
e7549107 444/*
e9576ca5
SC
445
446void wxMenu::SetLabel(int id, const wxString& label)
447{
519cb848
SC
448 Str255 maclabel ;
449 int index ;
e9576ca5
SC
450 wxMenuItem *item = FindItemForId(id) ;
451 if (item==NULL)
452 return;
453
519cb848
SC
454 index = MacGetIndexFromItem( item ) ;
455 if (index < 1)
456 return;
457
e9576ca5
SC
458 if (item->GetSubMenu()==NULL)
459 {
519cb848 460 wxMacBuildMenuString( maclabel , NULL , NULL , label , false );
e7549107 461 ::SetMenuItemText( m_hMenu , index , maclabel ) ;
e9576ca5
SC
462 }
463 else
464 {
519cb848 465 wxMacBuildMenuString( maclabel , NULL , NULL , label , false );
e7549107 466 ::SetMenuItemText( m_hMenu , index , maclabel ) ;
e9576ca5
SC
467 }
468 item->SetName(label);
469}
470
471wxString wxMenu::GetLabel(int Id) const
472{
519cb848
SC
473 wxMenuItem *pItem = FindItemForId(Id) ;
474 return pItem->GetName() ;
e9576ca5
SC
475}
476
477// Finds the item id matching the given string, -1 if not found.
478int 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
504wxMenuItem *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
532void wxMenu::SetHelpString(int itemId, const wxString& helpString)
533{
534 wxMenuItem *item = FindItemForId (itemId);
535 if (item)
536 item->SetHelp(helpString);
537}
538
539wxString wxMenu::GetHelpString (int itemId) const
540{
541 wxMenuItem *item = FindItemForId (itemId);
542 wxString str("");
543 return (item == NULL) ? str : item->GetHelp();
544}
e7549107 545*/
e9576ca5 546
e7549107 547bool wxMenu::ProcessCommand(wxCommandEvent & event)
e9576ca5
SC
548{
549 bool processed = FALSE;
550
e7549107 551#if WXWIN_COMPATIBILITY
e9576ca5
SC
552 // Try a callback
553 if (m_callback)
554 {
e7549107
SC
555 (void)(*(m_callback))(*this, event);
556 processed = TRUE;
e9576ca5 557 }
e7549107 558#endif WXWIN_COMPATIBILITY
e9576ca5
SC
559
560 // Try the menu's event handler
561 if ( !processed && GetEventHandler())
562 {
e7549107 563 processed = GetEventHandler()->ProcessEvent(event);
e9576ca5 564 }
519cb848 565
e7549107
SC
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
580void 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;
e9576ca5
SC
587}
588
e7549107
SC
589void 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
596wxWindow *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}
519cb848 605
519cb848
SC
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
609int wxMenu::MacGetIndexFromId( int id )
610{
e7549107
SC
611 size_t pos;
612 wxMenuItemList::Node *node = GetMenuItems().GetFirst();
613 for ( pos = 0; node; pos++ )
519cb848 614 {
e7549107
SC
615 if ( node->GetData()->GetId() == id )
616 break;
519cb848 617
e7549107
SC
618 node = node->GetNext();
619 }
620
519cb848
SC
621 if (!node)
622 return 0;
623
624 return pos + 1 ;
625}
626
627int wxMenu::MacGetIndexFromItem( wxMenuItem *pItem )
628{
e7549107
SC
629 size_t pos;
630 wxMenuItemList::Node *node = GetMenuItems().GetFirst();
631 for ( pos = 0; node; pos++ )
519cb848 632 {
e7549107
SC
633 if ( node->GetData() == pItem )
634 break;
635
636 node = node->GetNext();
519cb848
SC
637 }
638
639 if (!node)
640 return 0;
641
642 return pos + 1 ;
643}
644
645void wxMenu::MacEnableMenu( bool bDoEnable )
646{
519cb848 647 if ( bDoEnable )
e7549107 648 UMAEnableMenuItem( m_hMenu , 0 ) ;
519cb848 649 else
e7549107 650 UMADisableMenuItem( m_hMenu , 0 ) ;
519cb848
SC
651
652 ::DrawMenuBar() ;
653}
654
655bool wxMenu::MacMenuSelect( wxEvtHandler* handler, long when , int macMenuId, int macMenuItemNum )
656{
657 int pos;
658 wxNode *node;
659
660 if ( m_macMenuId == macMenuId )
661 {
e7549107 662 node = GetMenuItems().Nth(macMenuItemNum-1);
519cb848
SC
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);
e7549107 670 event.SetInt( pItem->GetId() );
51abe921
SC
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 }
519cb848
SC
693 return true ;
694 }
695 }
696 else if ( macMenuId == kHMHelpMenuID )
697 {
698 int menuItem = formerHelpMenuItems ;
e7549107 699 for (pos = 0, node = GetMenuItems().First(); node; node = node->Next(), pos++)
519cb848
SC
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);
e7549107 717 event.SetInt( pItem->GetId() );
51abe921
SC
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 }
519cb848
SC
739 return true ;
740 }
741 }
742 }
743 }
744
e7549107 745 for (pos = 0, node = GetMenuItems().First(); node; node = node->Next(), pos++)
519cb848 746 {
e7549107 747 wxMenuItem * pItem = (wxMenuItem *) node->Data() ;
519cb848 748
e7549107 749 wxMenu *pSubMenu = pItem->GetSubMenu() ;
519cb848
SC
750 if ( pSubMenu != NULL )
751 {
752 if ( pSubMenu->MacMenuSelect( handler , when , macMenuId , macMenuItemNum ) )
753 return true ;
754 }
755 }
756
757 return false ;
e9576ca5
SC
758}
759
760// Menu Bar
519cb848
SC
761
762/*
763
764Mac Implementation note :
765
766The Mac has only one global menubar, so we attempt to install the currently
767active menubar from a frame, we currently don't take into account mdi-frames
768which would ask for menu-merging
769
770Secondly there is no mac api for changing a menubar that is not the current
771menubar, so we have to wait for preparing the actual menubar until the
772wxMenubar is to be used
773
774We can in subsequent versions use MacInstallMenuBar to provide some sort of
775auto-merge for MDI in case this will be necessary
776
777*/
778
779wxMenuBar* wxMenuBar::s_macInstalledMenuBar = NULL ;
780
e7549107 781void wxMenuBar::Init()
e9576ca5
SC
782{
783 m_eventHandler = this;
e9576ca5 784 m_menuBarFrame = NULL;
e9576ca5
SC
785}
786
51abe921
SC
787wxMenuBar::wxMenuBar()
788{
789 Init();
790}
791
792wxMenuBar::wxMenuBar( long WXUNUSED(style) )
793{
794 Init();
795}
796
e7549107
SC
797
798wxMenuBar::wxMenuBar(int count, wxMenu *menus[], const wxString titles[])
e9576ca5 799{
e7549107
SC
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 }
e9576ca5
SC
811}
812
813wxMenuBar::~wxMenuBar()
814{
519cb848
SC
815 if (s_macInstalledMenuBar == this)
816 {
817 ::ClearMenuBar();
818 s_macInstalledMenuBar = NULL;
819 }
820
e7549107
SC
821}
822
823void wxMenuBar::Refresh()
824{
825 wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") );
e9576ca5 826
e7549107 827 DrawMenuBar();
e9576ca5
SC
828}
829
51abe921
SC
830#if wxUSE_ACCEL
831
832void 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
519cb848
SC
861void 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
e7549107 872 for (int i = 0; i < m_menus.GetCount(); i++)
519cb848
SC
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
e7549107 894 for (pos = 0 , node = menu->GetMenuItems().First(); node; node = node->Next(), pos++)
519cb848
SC
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 ;
e7549107 905 wxMacBuildMenuString( label , NULL , NULL , item->GetText(), item->GetId() != wxApp::s_macAboutMenuItemId); // no shortcut in about menu
519cb848
SC
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 );
e7549107
SC
929 UMASetMenuTitle( menu->GetHMenu() , label ) ;
930 for (pos = 0, node = menu->GetMenuItems().First(); node; node = node->Next(), pos++)
519cb848
SC
931 {
932 item = (wxMenuItem *)node->Data();
933 subMenu = item->GetSubMenu() ;
934 if (subMenu)
935 {
e7549107 936 ::InsertMenu( subMenu->GetHMenu() , -1 ) ;
519cb848
SC
937 }
938 }
e7549107 939 ::InsertMenu(m_menus[i]->GetHMenu(), 0);
519cb848
SC
940 }
941 }
942 ::DrawMenuBar() ;
943
944 s_macInstalledMenuBar = this;
945}
946
e7549107 947void wxMenuBar::EnableTop(size_t pos, bool enable)
e9576ca5 948{
e7549107
SC
949 wxCHECK_RET( IsAttached(), wxT("doesn't work with unattached menubars") );
950 m_menus[pos]->MacEnableMenu( enable ) ;
951 Refresh();
e9576ca5
SC
952}
953
e7549107 954void wxMenuBar::SetLabelTop(size_t pos, const wxString& label)
e9576ca5 955{
e7549107 956 wxCHECK_RET( pos < GetMenuCount(), wxT("invalid menu index") );
e9576ca5 957
e7549107 958 m_titles[pos] = label;
e9576ca5 959
e7549107
SC
960 if ( !IsAttached() )
961 {
e9576ca5 962 return;
e7549107 963 }
e9576ca5 964
519cb848 965 m_menus[pos]->SetTitle( label ) ;
e7549107 966 if (wxMenuBar::s_macInstalledMenuBar == this) // are we currently installed ?
519cb848 967 {
e7549107 968 ::SetMenuBar( GetMenuBar() ) ;
519cb848 969 ::InvalMenuBar() ;
519cb848 970 }
e9576ca5
SC
971}
972
e7549107 973wxString wxMenuBar::GetLabelTop(size_t pos) const
e9576ca5 974{
e7549107
SC
975 wxCHECK_MSG( pos < GetMenuCount(), wxEmptyString,
976 wxT("invalid menu index in wxMenuBar::GetLabelTop") );
519cb848 977
e7549107 978 return m_titles[pos];
e9576ca5
SC
979}
980
e7549107 981int wxMenuBar::FindMenu(const wxString& title)
e9576ca5 982{
e7549107 983 wxString menuTitle = wxStripMenuCodes(title);
e9576ca5 984
e7549107
SC
985 size_t count = GetMenuCount();
986 for ( size_t i = 0; i < count; i++ )
e9576ca5 987 {
e7549107
SC
988 wxString title = wxStripMenuCodes(m_titles[i]);
989 if ( menuTitle == title )
990 return i;
e9576ca5 991 }
e9576ca5 992
e7549107 993 return wxNOT_FOUND;
e9576ca5 994
e9576ca5
SC
995}
996
e7549107
SC
997
998// ---------------------------------------------------------------------------
999// wxMenuBar construction
1000// ---------------------------------------------------------------------------
1001
1002// ---------------------------------------------------------------------------
1003// wxMenuBar construction
1004// ---------------------------------------------------------------------------
1005
1006wxMenu *wxMenuBar::Replace(size_t pos, wxMenu *menu, const wxString& title)
e9576ca5 1007{
e7549107
SC
1008 wxMenu *menuOld = wxMenuBarBase::Replace(pos, menu, title);
1009 if ( !menuOld )
1010 return FALSE;
1011 m_titles[pos] = title;
e9576ca5 1012
e7549107 1013 if ( IsAttached() )
e9576ca5 1014 {
e7549107
SC
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 }
e9576ca5 1032
e9576ca5 1033
e7549107
SC
1034#if wxUSE_ACCEL
1035 if ( menuOld->HasAccels() || menu->HasAccels() )
1036 {
1037 // need to rebuild accell table
1038 RebuildAccelTable();
1039 }
1040#endif // wxUSE_ACCEL
e9576ca5 1041
e7549107 1042 Refresh();
e9576ca5 1043 }
e9576ca5 1044
e7549107 1045 return menuOld;
e9576ca5
SC
1046}
1047
e7549107 1048bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title)
e9576ca5 1049{
e7549107
SC
1050 if ( !wxMenuBarBase::Insert(pos, menu, title) )
1051 return FALSE;
e9576ca5 1052
e7549107 1053 m_titles.Insert(title, pos);
e9576ca5 1054
e7549107
SC
1055 menu->Attach(this);
1056
1057 if ( IsAttached() )
e9576ca5 1058 {
e7549107
SC
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() )
e9576ca5 1070 {
e7549107
SC
1071 // need to rebuild accell table
1072 RebuildAccelTable();
e9576ca5 1073 }
e7549107 1074#endif // wxUSE_ACCEL
e9576ca5 1075
e7549107 1076 Refresh();
e9576ca5 1077 }
e7549107
SC
1078
1079 return TRUE;
e9576ca5
SC
1080}
1081
519cb848
SC
1082void 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 {
e7549107 1096 for (int i = 0; i < m_menus.GetCount() ; i++)
519cb848 1097 {
e7549107 1098 if ( m_menus[i]->MacGetMenuId() == macMenuId ||
519cb848
SC
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
e7549107 1112 for (int i = 0; i < m_menus.GetCount(); i++)
519cb848
SC
1113 {
1114 if ( m_menus[i]->MacMenuSelect( handler , when , macMenuId , macMenuItemNum ) )
1115 {
1116 break ;
1117 }
51abe921 1118 }
519cb848
SC
1119 }
1120}
e9576ca5 1121
51abe921
SC
1122wxMenu *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
1153bool 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
1191int 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
1206wxMenuItem *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