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