]> git.saurik.com Git - wxWidgets.git/blame - src/mac/carbon/menu.cpp
limit recursion depth in DumpUDT() to prevent crashes when dumping linked lists
[wxWidgets.git] / src / mac / carbon / menu.cpp
CommitLineData
e9576ca5
SC
1/////////////////////////////////////////////////////////////////////////////
2// Name: menu.cpp
3// Purpose: wxMenu, wxMenuBar, wxMenuItem
a31a5f85 4// Author: Stefan Csomor
e9576ca5 5// Modified by:
a31a5f85 6// Created: 1998-01-01
e9576ca5 7// RCS-ID: $Id$
a31a5f85 8// Copyright: (c) Stefan Csomor
65571936 9// Licence: wxWindows licence
e9576ca5
SC
10/////////////////////////////////////////////////////////////////////////////
11
3d1a4878 12#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
eb22f2a6
GD
13#pragma implementation "menu.h"
14#pragma implementation "menuitem.h"
15#endif
16
e9576ca5
SC
17// ============================================================================
18// headers & declarations
19// ============================================================================
20
77ffb593 21// wxWidgets headers
e9576ca5
SC
22// -----------------
23
3d1a4878
SC
24#include "wx/wxprec.h"
25
03e11df5 26#include "wx/app.h"
e9576ca5
SC
27#include "wx/menu.h"
28#include "wx/menuitem.h"
03e11df5 29#include "wx/window.h"
e9576ca5
SC
30#include "wx/log.h"
31#include "wx/utils.h"
2b5f62a0 32#include "wx/frame.h"
e9576ca5 33
519cb848
SC
34#include "wx/mac/uma.h"
35
e9576ca5
SC
36// other standard headers
37// ----------------------
38#include <string.h>
39
2f1ae414 40#if !USE_SHARED_LIBRARY
e9576ca5
SC
41IMPLEMENT_DYNAMIC_CLASS(wxMenu, wxEvtHandler)
42IMPLEMENT_DYNAMIC_CLASS(wxMenuBar, wxEvtHandler)
2f1ae414 43#endif
e9576ca5 44
519cb848 45// the (popup) menu title has this special id
de5914a1 46static const int idMenuTitle = -3;
519cb848
SC
47
48const short kwxMacMenuBarResource = 1 ;
49const short kwxMacAppleMenuId = 1 ;
50
de5914a1
SC
51
52// Find an item given the Macintosh Menu Reference
53
71f2fb52 54#if KEY_wxList_DEPRECATED
de5914a1
SC
55wxList wxWinMacMenuList(wxKEY_INTEGER);
56wxMenu *wxFindMenuFromMacMenu(MenuRef inMenuRef)
57{
58 wxNode *node = wxWinMacMenuList.Find((long)inMenuRef);
59 if (!node)
60 return NULL;
61 return (wxMenu *)node->GetData();
62}
63
64void wxAssociateMenuWithMacMenu(MenuRef inMenuRef, wxMenu *menu) ;
65void wxAssociateMenuWithMacMenu(MenuRef inMenuRef, wxMenu *menu)
66{
67 // adding NULL MenuRef is (first) surely a result of an error and
68 // (secondly) breaks menu command processing
69 wxCHECK_RET( inMenuRef != (MenuRef) NULL, wxT("attempt to add a NULL MenuRef to menu list") );
70
71 if ( !wxWinMacMenuList.Find((long)inMenuRef) )
72 wxWinMacMenuList.Append((long)inMenuRef, menu);
73}
74
75void wxRemoveMacMenuAssociation(wxMenu *menu) ;
76void wxRemoveMacMenuAssociation(wxMenu *menu)
77{
78 wxWinMacMenuList.DeleteObject(menu);
79}
71f2fb52
RN
80#else
81
82WX_DECLARE_HASH_MAP(MenuRef, wxMenu*, wxPointerHash, wxPointerEqual, MacMenuMap);
83
84static MacMenuMap wxWinMacMenuList;
85
86wxMenu *wxFindMenuFromMacMenu(MenuRef inMenuRef)
87{
88 MacMenuMap::iterator node = wxWinMacMenuList.find(inMenuRef);
89
90 return (node == wxWinMacMenuList.end()) ? NULL : node->second;
91}
92
93void wxAssociateMenuWithMacMenu(MenuRef inMenuRef, wxMenu *menu) ;
94void wxAssociateMenuWithMacMenu(MenuRef inMenuRef, wxMenu *menu)
95{
96 // adding NULL MenuRef is (first) surely a result of an error and
97 // (secondly) breaks menu command processing
98 wxCHECK_RET( inMenuRef != (MenuRef) NULL, wxT("attempt to add a NULL MenuRef to menu list") );
de5914a1 99
71f2fb52
RN
100 wxWinMacMenuList[inMenuRef] = menu;
101}
102
103void wxRemoveMacMenuAssociation(wxMenu *menu) ;
104void wxRemoveMacMenuAssociation(wxMenu *menu)
105{
106 // iterate over all the elements in the class
107 MacMenuMap::iterator it;
108 for ( it = wxWinMacMenuList.begin(); it != wxWinMacMenuList.end(); ++it )
109 {
110 if ( it->second == menu )
111 {
112 wxWinMacMenuList.erase(it);
113 break;
114 }
115 }
116}
117#endif // deprecated wxList
de5914a1 118
e9576ca5
SC
119// ============================================================================
120// implementation
121// ============================================================================
7c15086c
SC
122static void wxMenubarUnsetInvokingWindow( wxMenu *menu ) ;
123static void wxMenubarSetInvokingWindow( wxMenu *menu, wxWindow *win );
519cb848 124
e9576ca5
SC
125// Menus
126
127// Construct a menu with optional title (then use append)
519cb848 128
f5c6eb5c 129#ifdef __DARWIN__
82ca6dbc
GD
130short wxMenu::s_macNextMenuId = 3 ;
131#else
519cb848 132short wxMenu::s_macNextMenuId = 2 ;
82ca6dbc 133#endif
519cb848 134
71f2fb52
RN
135static
136wxMenu *
137_wxMenuAt(const wxMenuList &menuList, size_t pos)
138{
139 wxMenuList::compatibility_iterator menuIter = menuList.GetFirst();
140
141 while (pos-- > 0) menuIter = menuIter->GetNext();
142
143 return menuIter->GetData() ;
144}
145
e7549107 146void wxMenu::Init()
e9576ca5 147{
e7549107 148 m_doBreak = FALSE;
7c15086c 149 m_startRadioGroup = -1;
e7549107
SC
150
151 // create the menu
58751a86 152 m_macMenuId = s_macNextMenuId++;
a9412f8f 153 m_hMenu = UMANewMenu(m_macMenuId, m_title, wxFont::GetDefaultEncoding() );
e7549107
SC
154
155 if ( !m_hMenu )
156 {
22026088 157 wxLogLastError(wxT("UMANewMenu failed"));
e7549107
SC
158 }
159
de5914a1
SC
160 wxAssociateMenuWithMacMenu( (MenuRef)m_hMenu , this ) ;
161
e7549107
SC
162 // if we have a title, insert it in the beginning of the menu
163 if ( !!m_title )
e9576ca5 164 {
519cb848 165 Append(idMenuTitle, m_title) ;
e9576ca5
SC
166 AppendSeparator() ;
167 }
e7549107 168}
e9576ca5 169
e7549107
SC
170wxMenu::~wxMenu()
171{
de5914a1 172 wxRemoveMacMenuAssociation( this ) ;
e40298d5
JS
173 if (MAC_WXHMENU(m_hMenu))
174 ::DisposeMenu(MAC_WXHMENU(m_hMenu));
e9576ca5
SC
175}
176
e7549107 177void wxMenu::Break()
e9576ca5 178{
e40298d5 179 // not available on the mac platform
e7549107 180}
e9576ca5 181
7c15086c 182void wxMenu::Attach(wxMenuBarBase *menubar)
e7549107 183{
7c15086c 184 wxMenuBase::Attach(menubar);
e7549107 185
7c15086c 186 EndRadioGroup();
e9576ca5
SC
187}
188
e9576ca5 189// function appends a new item or submenu to the menu
e7549107
SC
190// append a new item or submenu to the menu
191bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos)
e9576ca5 192{
427ff662 193 wxASSERT_MSG( pItem != NULL, wxT("can't append NULL item to the menu") );
e9576ca5 194
e40298d5
JS
195 if ( pItem->IsSeparator() )
196 {
197 if ( pos == (size_t)-1 )
198 MacAppendMenu(MAC_WXHMENU(m_hMenu), "\p-");
199 else
200 MacInsertMenuItem(MAC_WXHMENU(m_hMenu), "\p-" , pos);
201 }
58751a86 202 else
e40298d5
JS
203 {
204 wxMenu *pSubMenu = pItem->GetSubMenu() ;
205 if ( pSubMenu != NULL )
206 {
427ff662 207 wxASSERT_MSG( pSubMenu->m_hMenu != NULL , wxT("invalid submenu added"));
e40298d5 208 pSubMenu->m_menuParent = this ;
58751a86 209
4224f059 210 if (wxMenuBar::MacGetInstalledMenuBar() == GetMenuBar())
d46342af
SC
211 {
212 pSubMenu->MacBeforeDisplay( true ) ;
213 }
58751a86 214
e40298d5 215 if ( pos == (size_t)-1 )
a9412f8f 216 UMAAppendSubMenuItem(MAC_WXHMENU(m_hMenu), pItem->GetText(), wxFont::GetDefaultEncoding() , pSubMenu->m_macMenuId);
e40298d5 217 else
a9412f8f 218 UMAInsertSubMenuItem(MAC_WXHMENU(m_hMenu), pItem->GetText(), wxFont::GetDefaultEncoding() , pos, pSubMenu->m_macMenuId);
e40298d5
JS
219 pItem->UpdateItemBitmap() ;
220 pItem->UpdateItemStatus() ;
221 }
222 else
223 {
224 if ( pos == (size_t)-1 )
225 {
a9412f8f 226 UMAAppendMenuItem(MAC_WXHMENU(m_hMenu), wxT("a") , wxFont::GetDefaultEncoding() );
e40298d5
JS
227 pos = CountMenuItems(MAC_WXHMENU(m_hMenu)) ;
228 }
229 else
230 {
4ab107d7
SC
231 // MacOS counts menu items from 1 and inserts after, therefore having the
232 // same effect as wx 0 based and inserting before, we must correct pos
233 // after however for updates to be correct
a9412f8f 234 UMAInsertMenuItem(MAC_WXHMENU(m_hMenu), wxT("a"), wxFont::GetDefaultEncoding(), pos);
4ab107d7 235 pos += 1 ;
e40298d5
JS
236 }
237
4ab107d7 238 SetMenuItemCommandID( MAC_WXHMENU(m_hMenu) , pos , pItem->GetId() ) ;
de5914a1 239 SetMenuItemRefCon( MAC_WXHMENU(m_hMenu) , pos , (UInt32) pItem ) ;
e40298d5
JS
240 pItem->UpdateItemText() ;
241 pItem->UpdateItemBitmap() ;
242 pItem->UpdateItemStatus() ;
243
58751a86 244 if ( pItem->GetId() == idMenuTitle )
e40298d5
JS
245 {
246 UMAEnableMenuItem(MAC_WXHMENU(m_hMenu) , pos , false ) ;
247 }
248 }
249 }
e7549107
SC
250 // if we're already attached to the menubar, we must update it
251 if ( IsAttached() )
252 {
4224f059 253 GetMenuBar()->Refresh();
e7549107 254 }
e40298d5 255 return TRUE ;
e9576ca5
SC
256}
257
7c15086c
SC
258void wxMenu::EndRadioGroup()
259{
260 // we're not inside a radio group any longer
261 m_startRadioGroup = -1;
262}
263
9add9367 264wxMenuItem* wxMenu::DoAppend(wxMenuItem *item)
e9576ca5 265{
9add9367 266 wxCHECK_MSG( item, NULL, _T("NULL item in wxMenu::DoAppend") );
7c15086c
SC
267
268 bool check = FALSE;
269
270 if ( item->GetKind() == wxITEM_RADIO )
271 {
272 int count = GetMenuItemCount();
273
274 if ( m_startRadioGroup == -1 )
275 {
276 // start a new radio group
277 m_startRadioGroup = count;
278
279 // for now it has just one element
280 item->SetAsRadioGroupStart();
281 item->SetRadioGroupEnd(m_startRadioGroup);
282
283 // ensure that we have a checked item in the radio group
284 check = TRUE;
285 }
286 else // extend the current radio group
287 {
288 // we need to update its end item
289 item->SetRadioGroupStart(m_startRadioGroup);
71f2fb52 290 wxMenuItemList::compatibility_iterator node = GetMenuItems().Item(m_startRadioGroup);
7c15086c
SC
291
292 if ( node )
293 {
294 node->GetData()->SetRadioGroupEnd(count);
295 }
296 else
297 {
298 wxFAIL_MSG( _T("where is the radio group start item?") );
299 }
300 }
301 }
302 else // not a radio item
303 {
304 EndRadioGroup();
305 }
306
307 if ( !wxMenuBase::DoAppend(item) || !DoInsertOrAppend(item) )
308 {
9add9367 309 return NULL;
7c15086c
SC
310 }
311
312 if ( check )
313 {
314 // check the item initially
315 item->Check(TRUE);
316 }
317
9add9367 318 return item;
e9576ca5
SC
319}
320
9add9367 321wxMenuItem* wxMenu::DoInsert(size_t pos, wxMenuItem *item)
e9576ca5 322{
9add9367
RD
323 if (wxMenuBase::DoInsert(pos, item) && DoInsertOrAppend(item, pos))
324 return item;
325 else
326 return NULL;
e9576ca5
SC
327}
328
e7549107 329wxMenuItem *wxMenu::DoRemove(wxMenuItem *item)
e9576ca5 330{
e7549107
SC
331 // we need to find the items position in the child list
332 size_t pos;
71f2fb52 333 wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
e7549107
SC
334 for ( pos = 0; node; pos++ )
335 {
336 if ( node->GetData() == item )
337 break;
e9576ca5 338
e7549107 339 node = node->GetNext();
e9576ca5
SC
340 }
341
e7549107
SC
342 // DoRemove() (unlike Remove) can only be called for existing item!
343 wxCHECK_MSG( node, NULL, wxT("bug in wxMenu::Remove logic") );
519cb848 344
e40298d5 345 ::DeleteMenuItem(MAC_WXHMENU(m_hMenu) , pos + 1);
e9576ca5 346
e7549107
SC
347 if ( IsAttached() )
348 {
7c15086c 349 // otherwise, the change won't be visible
4224f059 350 GetMenuBar()->Refresh();
e7549107 351 }
e9576ca5 352
e7549107
SC
353 // and from internal data structures
354 return wxMenuBase::DoRemove(item);
e9576ca5
SC
355}
356
e9576ca5
SC
357void wxMenu::SetTitle(const wxString& label)
358{
e40298d5 359 m_title = label ;
a9412f8f 360 UMASetMenuTitle(MAC_WXHMENU(m_hMenu) , label , wxFont::GetDefaultEncoding() ) ;
e9576ca5 361}
e7549107 362bool wxMenu::ProcessCommand(wxCommandEvent & event)
e9576ca5
SC
363{
364 bool processed = FALSE;
365
e9576ca5
SC
366 // Try the menu's event handler
367 if ( !processed && GetEventHandler())
368 {
e7549107 369 processed = GetEventHandler()->ProcessEvent(event);
e9576ca5 370 }
519cb848 371
e7549107
SC
372 // Try the window the menu was popped up from (and up through the
373 // hierarchy)
374 wxWindow *win = GetInvokingWindow();
375 if ( !processed && win )
376 processed = win->GetEventHandler()->ProcessEvent(event);
377
378 return processed;
379}
380
381
382// ---------------------------------------------------------------------------
383// other
384// ---------------------------------------------------------------------------
385
e7549107
SC
386wxWindow *wxMenu::GetWindow() const
387{
388 if ( m_invokingWindow != NULL )
389 return m_invokingWindow;
4224f059
DE
390 else if ( GetMenuBar() != NULL)
391 return (wxWindow *) GetMenuBar()->GetFrame();
e7549107
SC
392
393 return NULL;
394}
519cb848 395
58751a86 396// helper functions returning the mac menu position for a certain item, note that this is
519cb848
SC
397// mac-wise 1 - based, i.e. the first item has index 1 whereas on MSWin it has pos 0
398
58751a86 399int wxMenu::MacGetIndexFromId( int id )
519cb848 400{
e7549107 401 size_t pos;
71f2fb52 402 wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
e7549107 403 for ( pos = 0; node; pos++ )
519cb848 404 {
e7549107
SC
405 if ( node->GetData()->GetId() == id )
406 break;
519cb848 407
e7549107
SC
408 node = node->GetNext();
409 }
58751a86 410
519cb848 411 if (!node)
e40298d5 412 return 0;
58751a86 413
e40298d5 414 return pos + 1 ;
519cb848
SC
415}
416
58751a86 417int wxMenu::MacGetIndexFromItem( wxMenuItem *pItem )
519cb848 418{
e7549107 419 size_t pos;
71f2fb52 420 wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
e7549107 421 for ( pos = 0; node; pos++ )
519cb848 422 {
e7549107
SC
423 if ( node->GetData() == pItem )
424 break;
425
426 node = node->GetNext();
519cb848
SC
427 }
428
429 if (!node)
e40298d5 430 return 0;
58751a86 431
e40298d5 432 return pos + 1 ;
519cb848
SC
433}
434
58751a86 435void wxMenu::MacEnableMenu( bool bDoEnable )
519cb848 436{
e40298d5 437 UMAEnableMenuItem(MAC_WXHMENU(m_hMenu) , 0 , bDoEnable ) ;
58751a86 438
e40298d5 439 ::DrawMenuBar() ;
519cb848
SC
440}
441
d46342af
SC
442// MacOS needs to know about submenus somewhere within this menu
443// before it can be displayed , also hide special menu items like preferences
444// that are handled by the OS
58751a86 445void wxMenu::MacBeforeDisplay( bool isSubMenu )
d46342af
SC
446{
447 wxMenuItem* previousItem = NULL ;
5be55d56 448 size_t pos ;
71f2fb52 449 wxMenuItemList::compatibility_iterator node;
d46342af 450 wxMenuItem *item;
58751a86 451 for (pos = 0, node = GetMenuItems().GetFirst(); node; node = node->GetNext(), pos++)
d46342af
SC
452 {
453 item = (wxMenuItem *)node->GetData();
454 wxMenu* subMenu = item->GetSubMenu() ;
58751a86 455 if (subMenu)
d46342af
SC
456 {
457 subMenu->MacBeforeDisplay( true ) ;
458 }
fa4a6942 459 else // normal item
d46342af
SC
460 {
461 #if TARGET_CARBON
6524e8f0
SC
462 // what we do here is to hide the special items which are
463 // shown in the application menu anyhow -- it doesn't make
464 // sense to show them in their normal place as well
465 if ( item->GetId() == wxApp::s_macAboutMenuItemId ||
466 ( UMAGetSystemVersion() >= 0x1000 && (
467 item->GetId() == wxApp::s_macPreferencesMenuItemId ||
468 item->GetId() == wxApp::s_macExitMenuItemId ) ) )
469
d46342af 470 {
6524e8f0
SC
471 ChangeMenuItemAttributes( MAC_WXHMENU( GetHMenu() ),
472 pos + 1, kMenuItemAttrHidden, 0 );
473
474 // also check for a separator which was used just to
475 // separate this item from the others, so don't leave
476 // separator at the menu start or end nor 2 consecutive
477 // separators
478 wxMenuItemList::compatibility_iterator nextNode = node->GetNext();
479 wxMenuItem *next = nextNode ? nextNode->GetData() : NULL;
480
481 size_t posSeptoHide;
482 if ( !previousItem && next && next->IsSeparator() )
d46342af 483 {
6524e8f0
SC
484 // next (i.e. second as we must be first) item is
485 // the separator to hide
486 wxASSERT_MSG( pos == 0, _T("should be the menu start") );
487 posSeptoHide = 2;
488 }
489 else if ( GetMenuItems().GetCount() == pos + 1 &&
490 previousItem != NULL &&
491 previousItem->IsSeparator() )
492 {
493 // prev item is a trailing separator we want to hide
494 posSeptoHide = pos;
495 }
496 else if ( previousItem && previousItem->IsSeparator() &&
497 next && next->IsSeparator() )
498 {
499 // two consecutive separators, this is one too many
500 posSeptoHide = pos;
501 }
502 else // no separators to hide
503 {
504 posSeptoHide = 0;
505 }
fa4a6942 506
6524e8f0
SC
507 if ( posSeptoHide )
508 {
509 // hide the separator as well
510 ChangeMenuItemAttributes( MAC_WXHMENU( GetHMenu() ),
511 posSeptoHide,
512 kMenuItemAttrHidden,
513 0 );
d46342af
SC
514 }
515 }
fa4a6942 516 #endif // TARGET_CARBON
d46342af
SC
517 }
518 previousItem = item ;
519 }
520
521 if ( isSubMenu )
522 ::InsertMenu(MAC_WXHMENU( GetHMenu()), -1);
523
524}
525// undo all changes from the MacBeforeDisplay call
58751a86 526void wxMenu::MacAfterDisplay( bool isSubMenu )
d46342af
SC
527{
528 if ( isSubMenu )
529 ::DeleteMenu(MacGetMenuId());
530
531 wxMenuItem* previousItem = NULL ;
532 int pos ;
71f2fb52 533 wxMenuItemList::compatibility_iterator node;
d46342af 534 wxMenuItem *item;
58751a86 535 for (pos = 0, node = GetMenuItems().GetFirst(); node; node = node->GetNext(), pos++)
d46342af
SC
536 {
537 item = (wxMenuItem *)node->GetData();
538 wxMenu* subMenu = item->GetSubMenu() ;
58751a86 539 if (subMenu)
d46342af
SC
540 {
541 subMenu->MacAfterDisplay( true ) ;
542 }
543 else
544 {
545 // no need to undo hidings
546 }
547 previousItem = item ;
548 }
549}
550
e9576ca5 551// Menu Bar
519cb848 552
58751a86 553/*
519cb848
SC
554
555Mac Implementation note :
556
557The Mac has only one global menubar, so we attempt to install the currently
558active menubar from a frame, we currently don't take into account mdi-frames
559which would ask for menu-merging
560
58751a86 561Secondly there is no mac api for changing a menubar that is not the current
519cb848
SC
562menubar, so we have to wait for preparing the actual menubar until the
563wxMenubar is to be used
564
58751a86 565We can in subsequent versions use MacInstallMenuBar to provide some sort of
519cb848
SC
566auto-merge for MDI in case this will be necessary
567
568*/
569
570wxMenuBar* wxMenuBar::s_macInstalledMenuBar = NULL ;
1b1d2207 571wxMenuBar* wxMenuBar::s_macCommonMenuBar = NULL ;
519cb848 572
e7549107 573void wxMenuBar::Init()
e9576ca5
SC
574{
575 m_eventHandler = this;
e9576ca5 576 m_menuBarFrame = NULL;
7c15086c 577 m_invokingWindow = (wxWindow*) NULL;
e9576ca5
SC
578}
579
51abe921
SC
580wxMenuBar::wxMenuBar()
581{
582 Init();
583}
584
585wxMenuBar::wxMenuBar( long WXUNUSED(style) )
586{
587 Init();
588}
589
e7549107 590
294ea16d 591wxMenuBar::wxMenuBar(size_t count, wxMenu *menus[], const wxString titles[], long WXUNUSED(style))
e9576ca5 592{
e7549107
SC
593 Init();
594
595 m_titles.Alloc(count);
596
d2103c8c 597 for ( size_t i = 0; i < count; i++ )
e7549107
SC
598 {
599 m_menus.Append(menus[i]);
600 m_titles.Add(titles[i]);
601
602 menus[i]->Attach(this);
603 }
e9576ca5
SC
604}
605
606wxMenuBar::~wxMenuBar()
607{
1b1d2207
DE
608 if (s_macCommonMenuBar == this)
609 s_macCommonMenuBar = NULL;
e40298d5
JS
610 if (s_macInstalledMenuBar == this)
611 {
612 ::ClearMenuBar();
613 s_macInstalledMenuBar = NULL;
614 }
519cb848 615
e7549107
SC
616}
617
6d6da89c 618void wxMenuBar::Refresh(bool WXUNUSED(eraseBackground), const wxRect *WXUNUSED(rect))
e7549107
SC
619{
620 wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") );
e9576ca5 621
e7549107 622 DrawMenuBar();
e9576ca5
SC
623}
624
58751a86 625void wxMenuBar::MacInstallMenuBar()
519cb848 626{
2b8a6962
GD
627 if ( s_macInstalledMenuBar == this )
628 return ;
58751a86 629
53d92341
SC
630 MenuBarHandle menubar = NULL ;
631#if TARGET_API_MAC_OSX
632 menubar = NewHandleClear( 6 /* sizeof( MenuBarHeader ) */ ) ;
633#else
634 menubar = NewHandleClear( 12 ) ;
635 (*menubar)[3] = 0x0a ;
636#endif
2b8a6962 637 ::SetMenuBar( menubar ) ;
dd05f811 638 DisposeMenuBar( menubar ) ;
cda73c27
SC
639 MenuHandle appleMenu = NULL ;
640 char appleMenuTitle[3] = { 01 , kMenuAppleLogoFilledGlyph , 0 } ;
641
642 verify_noerr( CreateNewMenu( kwxMacAppleMenuId , 0 , &appleMenu ) ) ;
643 verify_noerr( SetMenuTitle( appleMenu , (ConstStr255Param) appleMenuTitle ) );
23c6ddc8 644
262ce38a
KH
645 // Add About/Preferences separator only on OS X
646 // KH/RN: Separator is always present on 10.3 but not on 10.2
647 // However, the change from 10.2 to 10.3 suggests it is preferred
648#if TARGET_API_MAC_OSX
e957d41c 649 MacInsertMenuItem( appleMenu , "\p-" , 0 ) ;
262ce38a
KH
650#endif
651
cda73c27
SC
652 MacInsertMenuItem( appleMenu , "\pAbout..." , 0 ) ;
653 MacInsertMenu( appleMenu , 0 ) ;
654
7c15086c 655 // clean-up the help menu before adding new items
a71dec77 656 static MenuHandle mh = NULL ;
6524e8f0
SC
657
658 if ( mh != NULL )
e40298d5 659 {
6524e8f0
SC
660 MenuItemIndex firstUserHelpMenuItem ;
661 if ( UMAGetHelpMenu( &mh , &firstUserHelpMenuItem) == noErr )
e40298d5 662 {
6524e8f0
SC
663 for ( int i = CountMenuItems( mh ) ; i >= firstUserHelpMenuItem ; --i )
664 {
665 DeleteMenuItem( mh , i ) ;
666 }
e40298d5 667 }
6524e8f0
SC
668 else
669 {
670 mh = NULL ;
671 }
e40298d5 672 }
7c15086c 673#if TARGET_CARBON
e40298d5
JS
674 if ( UMAGetSystemVersion() >= 0x1000 && wxApp::s_macPreferencesMenuItemId)
675 {
676 wxMenuItem *item = FindItem( wxApp::s_macPreferencesMenuItemId , NULL ) ;
677 if ( item == NULL || !(item->IsEnabled()) )
678 DisableMenuCommand( NULL , kHICommandPreferences ) ;
679 else
680 EnableMenuCommand( NULL , kHICommandPreferences ) ;
681 }
3e275c2d
KH
682 // Unlike preferences which may or may not exist, the Quit item should be always
683 // enabled unless it is added by the application and then disabled, otherwise
684 // a program would be required to add an item with wxID_EXIT in order to get the
685 // Quit menu item to be enabled, which seems a bit burdensome.
e9626c1b
KH
686 if ( UMAGetSystemVersion() >= 0x1000 && wxApp::s_macExitMenuItemId)
687 {
688 wxMenuItem *item = FindItem( wxApp::s_macExitMenuItemId , NULL ) ;
3e275c2d 689 if ( item != NULL && !(item->IsEnabled()) )
e9626c1b
KH
690 DisableMenuCommand( NULL , kHICommandQuit ) ;
691 else
692 EnableMenuCommand( NULL , kHICommandQuit ) ;
693 }
7c15086c 694#endif
6524e8f0
SC
695 wxMenuList::compatibility_iterator menuIter = m_menus.GetFirst();
696 //
697 for (size_t i = 0; i < m_menus.GetCount(); i++, menuIter = menuIter->GetNext())
698 {
71f2fb52 699 wxMenuItemList::compatibility_iterator node;
2b5f62a0
VZ
700 wxMenuItem *item;
701 int pos ;
71f2fb52 702 wxMenu* menu = menuIter->GetData() , *subMenu = NULL ;
e40298d5 703
427ff662 704 if( m_titles[i] == wxT("?") || m_titles[i] == wxT("&?") || m_titles[i] == wxApp::s_macHelpMenuTitleName )
e40298d5 705 {
6524e8f0 706 for (pos = 0 , node = menu->GetMenuItems().GetFirst(); node; node = node->GetNext(), pos++)
e40298d5 707 {
6524e8f0
SC
708 item = (wxMenuItem *)node->GetData();
709 subMenu = item->GetSubMenu() ;
58751a86 710 if (subMenu)
e40298d5
JS
711 {
712 // we don't support hierarchical menus in the help menu yet
713 }
58751a86 714 else
e40298d5 715 {
6524e8f0
SC
716 if ( item->GetId() != wxApp::s_macAboutMenuItemId )
717 {
718 if ( mh == NULL )
719 {
720 MenuItemIndex firstUserHelpMenuItem ;
721 if ( UMAGetHelpMenu( &mh , &firstUserHelpMenuItem) == noErr )
722 {
723 }
724 else
725 {
726 mh = NULL ;
727 break ;
728 }
729 }
730 }
e40298d5
JS
731 if ( item->IsSeparator() )
732 {
733 if ( mh )
734 MacAppendMenu(mh, "\p-" );
735 }
736 else
737 {
738 wxAcceleratorEntry* entry = wxGetAccelFromString( item->GetText() ) ;
739
740 if ( item->GetId() == wxApp::s_macAboutMenuItemId )
58751a86 741 {
6524e8f0
SC
742 // this will be taken care of below
743 }
e40298d5
JS
744 else
745 {
746 if ( mh )
747 {
a9412f8f 748 UMAAppendMenuItem(mh, item->GetText() , wxFont::GetDefaultEncoding(), entry);
e40298d5 749 SetMenuItemCommandID( mh , CountMenuItems(mh) , item->GetId() ) ;
de5914a1 750 SetMenuItemRefCon( mh , CountMenuItems(mh) , (UInt32)item ) ;
e40298d5
JS
751 }
752 }
58751a86 753
e40298d5
JS
754 delete entry ;
755 }
756 }
757 }
758 }
759 else
760 {
a9412f8f 761 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , m_titles[i], m_font.GetEncoding() ) ;
71f2fb52
RN
762 menu->MacBeforeDisplay(false) ;
763 ::InsertMenu(MAC_WXHMENU(_wxMenuAt(m_menus, i)->GetHMenu()), 0);
e40298d5
JS
764 }
765 }
6524e8f0
SC
766 // take care of the about menu item wherever it is
767 {
768 wxMenu* aboutMenu ;
769 wxMenuItem *aboutMenuItem = FindItem(wxApp::s_macAboutMenuItemId , &aboutMenu) ;
770 if ( aboutMenuItem )
771 {
772 wxAcceleratorEntry* entry = wxGetAccelFromString( aboutMenuItem->GetText() ) ;
773 UMASetMenuItemText( GetMenuHandle( kwxMacAppleMenuId ) , 1 , aboutMenuItem->GetText() , wxFont::GetDefaultEncoding() );
774 UMAEnableMenuItem( GetMenuHandle( kwxMacAppleMenuId ) , 1 , true );
775 SetMenuItemCommandID( GetMenuHandle( kwxMacAppleMenuId ) , 1 , aboutMenuItem->GetId() ) ;
776 SetMenuItemRefCon(GetMenuHandle( kwxMacAppleMenuId ) , 1 , (UInt32)aboutMenuItem ) ;
777 UMASetMenuItemShortcut( GetMenuHandle( kwxMacAppleMenuId ) , 1 , entry ) ;
778 }
779 }
e40298d5
JS
780 ::DrawMenuBar() ;
781 s_macInstalledMenuBar = this;
519cb848
SC
782}
783
e7549107 784void wxMenuBar::EnableTop(size_t pos, bool enable)
e9576ca5 785{
e7549107 786 wxCHECK_RET( IsAttached(), wxT("doesn't work with unattached menubars") );
71f2fb52 787 _wxMenuAt(m_menus, pos)->MacEnableMenu( enable ) ;
e7549107 788 Refresh();
e9576ca5
SC
789}
790
c393c740
JS
791bool wxMenuBar::Enable( bool enable)
792{
5059f192 793 wxCHECK_MSG( IsAttached(), false, wxT("doesn't work with unattached menubars") );
c393c740
JS
794 size_t i;
795 for (i = 0; i < GetMenuCount(); i++)
796 {
797 EnableTop(i, enable);
798 }
799 return true;
800}
801
e7549107 802void wxMenuBar::SetLabelTop(size_t pos, const wxString& label)
e9576ca5 803{
e7549107 804 wxCHECK_RET( pos < GetMenuCount(), wxT("invalid menu index") );
e9576ca5 805
e7549107 806 m_titles[pos] = label;
e9576ca5 807
e7549107
SC
808 if ( !IsAttached() )
809 {
e9576ca5 810 return;
e7549107 811 }
e9576ca5 812
71f2fb52
RN
813 _wxMenuAt(m_menus, pos)->SetTitle( label ) ;
814
e40298d5
JS
815 if (wxMenuBar::s_macInstalledMenuBar == this) // are we currently installed ?
816 {
817 ::SetMenuBar( GetMenuBar() ) ;
818 ::InvalMenuBar() ;
819 }
e9576ca5
SC
820}
821
e7549107 822wxString wxMenuBar::GetLabelTop(size_t pos) const
e9576ca5 823{
e7549107
SC
824 wxCHECK_MSG( pos < GetMenuCount(), wxEmptyString,
825 wxT("invalid menu index in wxMenuBar::GetLabelTop") );
519cb848 826
e7549107 827 return m_titles[pos];
e9576ca5
SC
828}
829
e7549107 830int wxMenuBar::FindMenu(const wxString& title)
e9576ca5 831{
e7549107 832 wxString menuTitle = wxStripMenuCodes(title);
e9576ca5 833
e7549107
SC
834 size_t count = GetMenuCount();
835 for ( size_t i = 0; i < count; i++ )
e9576ca5 836 {
e7549107
SC
837 wxString title = wxStripMenuCodes(m_titles[i]);
838 if ( menuTitle == title )
58751a86 839 return i;
e9576ca5 840 }
e9576ca5 841
e7549107 842 return wxNOT_FOUND;
e9576ca5 843
e9576ca5
SC
844}
845
e7549107 846
e7549107
SC
847// ---------------------------------------------------------------------------
848// wxMenuBar construction
849// ---------------------------------------------------------------------------
850
851wxMenu *wxMenuBar::Replace(size_t pos, wxMenu *menu, const wxString& title)
e9576ca5 852{
e7549107
SC
853 wxMenu *menuOld = wxMenuBarBase::Replace(pos, menu, title);
854 if ( !menuOld )
855 return FALSE;
856 m_titles[pos] = title;
e9576ca5 857
e7549107 858 if ( IsAttached() )
e9576ca5 859 {
e40298d5
JS
860 if (s_macInstalledMenuBar == this)
861 {
d46342af 862 menuOld->MacAfterDisplay( false ) ;
e40298d5
JS
863 ::DeleteMenu( menuOld->MacGetMenuId() /* m_menus[pos]->MacGetMenuId() */ ) ;
864 {
d46342af 865 menu->MacBeforeDisplay( false ) ;
a9412f8f 866 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , title , m_font.GetEncoding() ) ;
e40298d5
JS
867 if ( pos == m_menus.GetCount() - 1)
868 {
869 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , 0 ) ;
870 }
871 else
872 {
71f2fb52 873 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , _wxMenuAt(m_menus, pos+1)->MacGetMenuId() ) ;
e40298d5
JS
874 }
875 }
876 }
e9576ca5 877
e7549107 878 Refresh();
e9576ca5 879 }
0f4c4140
SC
880 if (m_invokingWindow)
881 wxMenubarSetInvokingWindow( menu, m_invokingWindow );
e9576ca5 882
e7549107 883 return menuOld;
e9576ca5
SC
884}
885
e7549107 886bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title)
e9576ca5 887{
e7549107
SC
888 if ( !wxMenuBarBase::Insert(pos, menu, title) )
889 return FALSE;
e9576ca5 890
e7549107 891 m_titles.Insert(title, pos);
e9576ca5 892
a9412f8f 893 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , title , m_font.GetEncoding() ) ;
e7549107 894
d46342af 895 if ( IsAttached() && s_macInstalledMenuBar == this )
e9576ca5 896 {
d46342af 897 if (s_macInstalledMenuBar == this)
e40298d5 898 {
d46342af
SC
899 menu->MacBeforeDisplay( false ) ;
900 if ( pos == (size_t) -1 || pos + 1 == m_menus.GetCount() )
901 {
902 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , 0 ) ;
903 }
904 else
905 {
71f2fb52 906 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , _wxMenuAt(m_menus, pos+1)->MacGetMenuId() ) ;
d46342af 907 }
e40298d5 908 }
e7549107 909 Refresh();
e9576ca5 910 }
0f4c4140
SC
911 if (m_invokingWindow)
912 wxMenubarSetInvokingWindow( menu, m_invokingWindow );
e7549107
SC
913
914 return TRUE;
e9576ca5
SC
915}
916
51abe921
SC
917wxMenu *wxMenuBar::Remove(size_t pos)
918{
919 wxMenu *menu = wxMenuBarBase::Remove(pos);
920 if ( !menu )
921 return NULL;
922
923 if ( IsAttached() )
924 {
e40298d5
JS
925 if (s_macInstalledMenuBar == this)
926 {
927 ::DeleteMenu( menu->MacGetMenuId() /* m_menus[pos]->MacGetMenuId() */ ) ;
928 }
51abe921 929
51abe921
SC
930 Refresh();
931 }
932
5fe38474 933 m_titles.RemoveAt(pos);
51abe921
SC
934
935 return menu;
936}
937
938bool wxMenuBar::Append(wxMenu *menu, const wxString& title)
939{
940 WXHMENU submenu = menu ? menu->GetHMenu() : 0;
941 wxCHECK_MSG( submenu, FALSE, wxT("can't append invalid menu to menubar") );
942
943 if ( !wxMenuBarBase::Append(menu, title) )
944 return FALSE;
945
51abe921 946 m_titles.Add(title);
58751a86 947
a9412f8f 948 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , title , m_font.GetEncoding() ) ;
51abe921
SC
949
950 if ( IsAttached() )
951 {
e40298d5
JS
952 if (s_macInstalledMenuBar == this)
953 {
94c80d52 954 menu->MacBeforeDisplay( false ) ;
e40298d5
JS
955 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , 0 ) ;
956 }
51abe921
SC
957
958 Refresh();
959 }
960
0f4c4140 961 // m_invokingWindow is set after wxFrame::SetMenuBar(). This call enables
7c15086c
SC
962 // adding menu later on.
963 if (m_invokingWindow)
964 wxMenubarSetInvokingWindow( menu, m_invokingWindow );
965
51abe921
SC
966 return TRUE;
967}
968
2b5f62a0
VZ
969static void wxMenubarUnsetInvokingWindow( wxMenu *menu )
970{
971 menu->SetInvokingWindow( (wxWindow*) NULL );
972
71f2fb52 973 wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst();
2b5f62a0
VZ
974 while (node)
975 {
976 wxMenuItem *menuitem = node->GetData();
977 if (menuitem->IsSubMenu())
978 wxMenubarUnsetInvokingWindow( menuitem->GetSubMenu() );
979 node = node->GetNext();
980 }
981}
982
983static void wxMenubarSetInvokingWindow( wxMenu *menu, wxWindow *win )
984{
985 menu->SetInvokingWindow( win );
986
71f2fb52 987 wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst();
2b5f62a0
VZ
988 while (node)
989 {
990 wxMenuItem *menuitem = node->GetData();
991 if (menuitem->IsSubMenu())
992 wxMenubarSetInvokingWindow( menuitem->GetSubMenu() , win );
993 node = node->GetNext();
994 }
995}
996
997void wxMenuBar::UnsetInvokingWindow()
998{
7c15086c 999 m_invokingWindow = (wxWindow*) NULL;
71f2fb52 1000 wxMenuList::compatibility_iterator node = m_menus.GetFirst();
2b5f62a0
VZ
1001 while (node)
1002 {
1003 wxMenu *menu = node->GetData();
1004 wxMenubarUnsetInvokingWindow( menu );
1005 node = node->GetNext();
1006 }
1007}
1008
1009void wxMenuBar::SetInvokingWindow(wxFrame *frame)
1010{
7c15086c 1011 m_invokingWindow = frame;
71f2fb52 1012 wxMenuList::compatibility_iterator node = m_menus.GetFirst();
2b5f62a0
VZ
1013 while (node)
1014 {
1015 wxMenu *menu = node->GetData();
1016 wxMenubarSetInvokingWindow( menu, frame );
1017 node = node->GetNext();
1018 }
1019}
1020
90b959ae 1021void wxMenuBar::Detach()
2f1ae414 1022{
90b959ae
SC
1023 wxMenuBarBase::Detach() ;
1024}
2f1ae414 1025
90b959ae
SC
1026void wxMenuBar::Attach(wxFrame *frame)
1027{
1028 wxMenuBarBase::Attach( frame ) ;
2f1ae414 1029}
51abe921
SC
1030// ---------------------------------------------------------------------------
1031// wxMenuBar searching for menu items
1032// ---------------------------------------------------------------------------
1033
1034// Find the itemString in menuString, and return the item id or wxNOT_FOUND
1035int wxMenuBar::FindMenuItem(const wxString& menuString,
1036 const wxString& itemString) const
1037{
1038 wxString menuLabel = wxStripMenuCodes(menuString);
1039 size_t count = GetMenuCount();
1040 for ( size_t i = 0; i < count; i++ )
1041 {
1042 wxString title = wxStripMenuCodes(m_titles[i]);
ef089805 1043 if ( menuLabel == title )
71f2fb52 1044 return _wxMenuAt(m_menus, i)->FindItem(itemString);
51abe921
SC
1045 }
1046
1047 return wxNOT_FOUND;
1048}
1049
1050wxMenuItem *wxMenuBar::FindItem(int id, wxMenu **itemMenu) const
1051{
1052 if ( itemMenu )
1053 *itemMenu = NULL;
1054
1055 wxMenuItem *item = NULL;
1056 size_t count = GetMenuCount();
1057 for ( size_t i = 0; !item && (i < count); i++ )
1058 {
71f2fb52 1059 item = _wxMenuAt(m_menus, i)->FindItem(id, itemMenu);
51abe921
SC
1060 }
1061
1062 return item;
1063}
1064
1065