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