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