]> git.saurik.com Git - wxWidgets.git/blame - src/mac/carbon/menu.cpp
Made ico files binary
[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
e9576ca5
SC
40IMPLEMENT_DYNAMIC_CLASS(wxMenu, wxEvtHandler)
41IMPLEMENT_DYNAMIC_CLASS(wxMenuBar, wxEvtHandler)
e9576ca5 42
519cb848 43// the (popup) menu title has this special id
de5914a1 44static const int idMenuTitle = -3;
519cb848
SC
45
46const short kwxMacMenuBarResource = 1 ;
47const short kwxMacAppleMenuId = 1 ;
48
de5914a1
SC
49
50// Find an item given the Macintosh Menu Reference
51
71f2fb52 52#if KEY_wxList_DEPRECATED
de5914a1
SC
53wxList wxWinMacMenuList(wxKEY_INTEGER);
54wxMenu *wxFindMenuFromMacMenu(MenuRef inMenuRef)
55{
56 wxNode *node = wxWinMacMenuList.Find((long)inMenuRef);
57 if (!node)
58 return NULL;
59 return (wxMenu *)node->GetData();
60}
61
62void wxAssociateMenuWithMacMenu(MenuRef inMenuRef, wxMenu *menu) ;
63void wxAssociateMenuWithMacMenu(MenuRef inMenuRef, wxMenu *menu)
64{
65 // adding NULL MenuRef is (first) surely a result of an error and
66 // (secondly) breaks menu command processing
67 wxCHECK_RET( inMenuRef != (MenuRef) NULL, wxT("attempt to add a NULL MenuRef to menu list") );
68
69 if ( !wxWinMacMenuList.Find((long)inMenuRef) )
70 wxWinMacMenuList.Append((long)inMenuRef, menu);
71}
72
73void wxRemoveMacMenuAssociation(wxMenu *menu) ;
74void wxRemoveMacMenuAssociation(wxMenu *menu)
75{
76 wxWinMacMenuList.DeleteObject(menu);
77}
71f2fb52
RN
78#else
79
80WX_DECLARE_HASH_MAP(MenuRef, wxMenu*, wxPointerHash, wxPointerEqual, MacMenuMap);
81
82static MacMenuMap wxWinMacMenuList;
83
84wxMenu *wxFindMenuFromMacMenu(MenuRef inMenuRef)
85{
86 MacMenuMap::iterator node = wxWinMacMenuList.find(inMenuRef);
87
88 return (node == wxWinMacMenuList.end()) ? NULL : node->second;
89}
90
91void wxAssociateMenuWithMacMenu(MenuRef inMenuRef, wxMenu *menu) ;
92void wxAssociateMenuWithMacMenu(MenuRef inMenuRef, wxMenu *menu)
93{
94 // adding NULL MenuRef is (first) surely a result of an error and
95 // (secondly) breaks menu command processing
96 wxCHECK_RET( inMenuRef != (MenuRef) NULL, wxT("attempt to add a NULL MenuRef to menu list") );
de5914a1 97
71f2fb52
RN
98 wxWinMacMenuList[inMenuRef] = menu;
99}
100
101void wxRemoveMacMenuAssociation(wxMenu *menu) ;
102void wxRemoveMacMenuAssociation(wxMenu *menu)
103{
104 // iterate over all the elements in the class
105 MacMenuMap::iterator it;
106 for ( it = wxWinMacMenuList.begin(); it != wxWinMacMenuList.end(); ++it )
107 {
108 if ( it->second == menu )
109 {
110 wxWinMacMenuList.erase(it);
111 break;
112 }
113 }
114}
115#endif // deprecated wxList
de5914a1 116
e9576ca5
SC
117// ============================================================================
118// implementation
119// ============================================================================
7c15086c
SC
120static void wxMenubarUnsetInvokingWindow( wxMenu *menu ) ;
121static void wxMenubarSetInvokingWindow( wxMenu *menu, wxWindow *win );
519cb848 122
e9576ca5
SC
123// Menus
124
125// Construct a menu with optional title (then use append)
519cb848 126
f5c6eb5c 127#ifdef __DARWIN__
82ca6dbc
GD
128short wxMenu::s_macNextMenuId = 3 ;
129#else
519cb848 130short wxMenu::s_macNextMenuId = 2 ;
82ca6dbc 131#endif
519cb848 132
71f2fb52
RN
133static
134wxMenu *
135_wxMenuAt(const wxMenuList &menuList, size_t pos)
136{
137 wxMenuList::compatibility_iterator menuIter = menuList.GetFirst();
138
139 while (pos-- > 0) menuIter = menuIter->GetNext();
140
141 return menuIter->GetData() ;
142}
143
e7549107 144void wxMenu::Init()
e9576ca5 145{
e7549107 146 m_doBreak = FALSE;
7c15086c 147 m_startRadioGroup = -1;
e7549107
SC
148
149 // create the menu
58751a86 150 m_macMenuId = s_macNextMenuId++;
a9412f8f 151 m_hMenu = UMANewMenu(m_macMenuId, m_title, wxFont::GetDefaultEncoding() );
e7549107
SC
152
153 if ( !m_hMenu )
154 {
22026088 155 wxLogLastError(wxT("UMANewMenu failed"));
e7549107
SC
156 }
157
de5914a1
SC
158 wxAssociateMenuWithMacMenu( (MenuRef)m_hMenu , this ) ;
159
e7549107
SC
160 // if we have a title, insert it in the beginning of the menu
161 if ( !!m_title )
e9576ca5 162 {
519cb848 163 Append(idMenuTitle, m_title) ;
e9576ca5
SC
164 AppendSeparator() ;
165 }
e7549107 166}
e9576ca5 167
e7549107
SC
168wxMenu::~wxMenu()
169{
de5914a1 170 wxRemoveMacMenuAssociation( this ) ;
e40298d5
JS
171 if (MAC_WXHMENU(m_hMenu))
172 ::DisposeMenu(MAC_WXHMENU(m_hMenu));
e9576ca5
SC
173}
174
e7549107 175void wxMenu::Break()
e9576ca5 176{
e40298d5 177 // not available on the mac platform
e7549107 178}
e9576ca5 179
7c15086c 180void wxMenu::Attach(wxMenuBarBase *menubar)
e7549107 181{
7c15086c 182 wxMenuBase::Attach(menubar);
e7549107 183
7c15086c 184 EndRadioGroup();
e9576ca5
SC
185}
186
e9576ca5 187// function appends a new item or submenu to the menu
e7549107
SC
188// append a new item or submenu to the menu
189bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos)
e9576ca5 190{
427ff662 191 wxASSERT_MSG( pItem != NULL, wxT("can't append NULL item to the menu") );
e9576ca5 192
e40298d5
JS
193 if ( pItem->IsSeparator() )
194 {
195 if ( pos == (size_t)-1 )
196 MacAppendMenu(MAC_WXHMENU(m_hMenu), "\p-");
197 else
198 MacInsertMenuItem(MAC_WXHMENU(m_hMenu), "\p-" , pos);
199 }
58751a86 200 else
e40298d5
JS
201 {
202 wxMenu *pSubMenu = pItem->GetSubMenu() ;
203 if ( pSubMenu != NULL )
204 {
427ff662 205 wxASSERT_MSG( pSubMenu->m_hMenu != NULL , wxT("invalid submenu added"));
e40298d5 206 pSubMenu->m_menuParent = this ;
58751a86 207
4224f059 208 if (wxMenuBar::MacGetInstalledMenuBar() == GetMenuBar())
d46342af
SC
209 {
210 pSubMenu->MacBeforeDisplay( true ) ;
211 }
58751a86 212
e40298d5 213 if ( pos == (size_t)-1 )
a9412f8f 214 UMAAppendSubMenuItem(MAC_WXHMENU(m_hMenu), pItem->GetText(), wxFont::GetDefaultEncoding() , pSubMenu->m_macMenuId);
e40298d5 215 else
a9412f8f 216 UMAInsertSubMenuItem(MAC_WXHMENU(m_hMenu), pItem->GetText(), wxFont::GetDefaultEncoding() , pos, pSubMenu->m_macMenuId);
e40298d5
JS
217 pItem->UpdateItemBitmap() ;
218 pItem->UpdateItemStatus() ;
219 }
220 else
221 {
222 if ( pos == (size_t)-1 )
223 {
a9412f8f 224 UMAAppendMenuItem(MAC_WXHMENU(m_hMenu), wxT("a") , wxFont::GetDefaultEncoding() );
e40298d5
JS
225 pos = CountMenuItems(MAC_WXHMENU(m_hMenu)) ;
226 }
227 else
228 {
4ab107d7
SC
229 // MacOS counts menu items from 1 and inserts after, therefore having the
230 // same effect as wx 0 based and inserting before, we must correct pos
231 // after however for updates to be correct
a9412f8f 232 UMAInsertMenuItem(MAC_WXHMENU(m_hMenu), wxT("a"), wxFont::GetDefaultEncoding(), pos);
4ab107d7 233 pos += 1 ;
e40298d5
JS
234 }
235
4ab107d7 236 SetMenuItemCommandID( MAC_WXHMENU(m_hMenu) , pos , pItem->GetId() ) ;
de5914a1 237 SetMenuItemRefCon( MAC_WXHMENU(m_hMenu) , pos , (UInt32) pItem ) ;
e40298d5
JS
238 pItem->UpdateItemText() ;
239 pItem->UpdateItemBitmap() ;
240 pItem->UpdateItemStatus() ;
241
58751a86 242 if ( pItem->GetId() == idMenuTitle )
e40298d5
JS
243 {
244 UMAEnableMenuItem(MAC_WXHMENU(m_hMenu) , pos , false ) ;
245 }
246 }
247 }
e7549107 248 // if we're already attached to the menubar, we must update it
b6d4a1af 249 if ( IsAttached() && GetMenuBar()->IsAttached() )
e7549107 250 {
4224f059 251 GetMenuBar()->Refresh();
e7549107 252 }
e40298d5 253 return TRUE ;
e9576ca5
SC
254}
255
7c15086c
SC
256void wxMenu::EndRadioGroup()
257{
258 // we're not inside a radio group any longer
259 m_startRadioGroup = -1;
260}
261
9add9367 262wxMenuItem* wxMenu::DoAppend(wxMenuItem *item)
e9576ca5 263{
9add9367 264 wxCHECK_MSG( item, NULL, _T("NULL item in wxMenu::DoAppend") );
7c15086c
SC
265
266 bool check = FALSE;
267
268 if ( item->GetKind() == wxITEM_RADIO )
269 {
270 int count = GetMenuItemCount();
271
272 if ( m_startRadioGroup == -1 )
273 {
274 // start a new radio group
275 m_startRadioGroup = count;
276
277 // for now it has just one element
278 item->SetAsRadioGroupStart();
279 item->SetRadioGroupEnd(m_startRadioGroup);
280
281 // ensure that we have a checked item in the radio group
282 check = TRUE;
283 }
284 else // extend the current radio group
285 {
286 // we need to update its end item
287 item->SetRadioGroupStart(m_startRadioGroup);
71f2fb52 288 wxMenuItemList::compatibility_iterator node = GetMenuItems().Item(m_startRadioGroup);
7c15086c
SC
289
290 if ( node )
291 {
292 node->GetData()->SetRadioGroupEnd(count);
293 }
294 else
295 {
296 wxFAIL_MSG( _T("where is the radio group start item?") );
297 }
298 }
299 }
300 else // not a radio item
301 {
302 EndRadioGroup();
303 }
304
305 if ( !wxMenuBase::DoAppend(item) || !DoInsertOrAppend(item) )
306 {
9add9367 307 return NULL;
7c15086c
SC
308 }
309
310 if ( check )
311 {
312 // check the item initially
313 item->Check(TRUE);
314 }
315
9add9367 316 return item;
e9576ca5
SC
317}
318
9add9367 319wxMenuItem* wxMenu::DoInsert(size_t pos, wxMenuItem *item)
e9576ca5 320{
9add9367
RD
321 if (wxMenuBase::DoInsert(pos, item) && DoInsertOrAppend(item, pos))
322 return item;
323 else
324 return NULL;
e9576ca5
SC
325}
326
e7549107 327wxMenuItem *wxMenu::DoRemove(wxMenuItem *item)
e9576ca5 328{
e7549107
SC
329 // we need to find the items position in the child list
330 size_t pos;
71f2fb52 331 wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
e7549107
SC
332 for ( pos = 0; node; pos++ )
333 {
334 if ( node->GetData() == item )
335 break;
e9576ca5 336
e7549107 337 node = node->GetNext();
e9576ca5
SC
338 }
339
e7549107
SC
340 // DoRemove() (unlike Remove) can only be called for existing item!
341 wxCHECK_MSG( node, NULL, wxT("bug in wxMenu::Remove logic") );
519cb848 342
e40298d5 343 ::DeleteMenuItem(MAC_WXHMENU(m_hMenu) , pos + 1);
e9576ca5 344
b6d4a1af 345 if ( IsAttached() && GetMenuBar()->IsAttached() )
e7549107 346 {
7c15086c 347 // otherwise, the change won't be visible
4224f059 348 GetMenuBar()->Refresh();
e7549107 349 }
e9576ca5 350
e7549107
SC
351 // and from internal data structures
352 return wxMenuBase::DoRemove(item);
e9576ca5
SC
353}
354
e9576ca5
SC
355void wxMenu::SetTitle(const wxString& label)
356{
e40298d5 357 m_title = label ;
a9412f8f 358 UMASetMenuTitle(MAC_WXHMENU(m_hMenu) , label , wxFont::GetDefaultEncoding() ) ;
e9576ca5 359}
e7549107 360bool wxMenu::ProcessCommand(wxCommandEvent & event)
e9576ca5
SC
361{
362 bool processed = FALSE;
363
e9576ca5
SC
364 // Try the menu's event handler
365 if ( !processed && GetEventHandler())
366 {
e7549107 367 processed = GetEventHandler()->ProcessEvent(event);
e9576ca5 368 }
519cb848 369
e7549107
SC
370 // Try the window the menu was popped up from (and up through the
371 // hierarchy)
372 wxWindow *win = GetInvokingWindow();
373 if ( !processed && win )
374 processed = win->GetEventHandler()->ProcessEvent(event);
375
376 return processed;
377}
378
379
380// ---------------------------------------------------------------------------
381// other
382// ---------------------------------------------------------------------------
383
e7549107
SC
384wxWindow *wxMenu::GetWindow() const
385{
386 if ( m_invokingWindow != NULL )
387 return m_invokingWindow;
4224f059
DE
388 else if ( GetMenuBar() != NULL)
389 return (wxWindow *) GetMenuBar()->GetFrame();
e7549107
SC
390
391 return NULL;
392}
519cb848 393
58751a86 394// helper functions returning the mac menu position for a certain item, note that this is
519cb848
SC
395// mac-wise 1 - based, i.e. the first item has index 1 whereas on MSWin it has pos 0
396
58751a86 397int wxMenu::MacGetIndexFromId( int id )
519cb848 398{
e7549107 399 size_t pos;
71f2fb52 400 wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
e7549107 401 for ( pos = 0; node; pos++ )
519cb848 402 {
e7549107
SC
403 if ( node->GetData()->GetId() == id )
404 break;
519cb848 405
e7549107
SC
406 node = node->GetNext();
407 }
58751a86 408
519cb848 409 if (!node)
e40298d5 410 return 0;
58751a86 411
e40298d5 412 return pos + 1 ;
519cb848
SC
413}
414
58751a86 415int wxMenu::MacGetIndexFromItem( wxMenuItem *pItem )
519cb848 416{
e7549107 417 size_t pos;
71f2fb52 418 wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
e7549107 419 for ( pos = 0; node; pos++ )
519cb848 420 {
e7549107
SC
421 if ( node->GetData() == pItem )
422 break;
423
424 node = node->GetNext();
519cb848
SC
425 }
426
427 if (!node)
e40298d5 428 return 0;
58751a86 429
e40298d5 430 return pos + 1 ;
519cb848
SC
431}
432
58751a86 433void wxMenu::MacEnableMenu( bool bDoEnable )
519cb848 434{
e40298d5 435 UMAEnableMenuItem(MAC_WXHMENU(m_hMenu) , 0 , bDoEnable ) ;
58751a86 436
e40298d5 437 ::DrawMenuBar() ;
519cb848
SC
438}
439
d46342af
SC
440// MacOS needs to know about submenus somewhere within this menu
441// before it can be displayed , also hide special menu items like preferences
442// that are handled by the OS
58751a86 443void wxMenu::MacBeforeDisplay( bool isSubMenu )
d46342af
SC
444{
445 wxMenuItem* previousItem = NULL ;
5be55d56 446 size_t pos ;
71f2fb52 447 wxMenuItemList::compatibility_iterator node;
d46342af 448 wxMenuItem *item;
58751a86 449 for (pos = 0, node = GetMenuItems().GetFirst(); node; node = node->GetNext(), pos++)
d46342af
SC
450 {
451 item = (wxMenuItem *)node->GetData();
452 wxMenu* subMenu = item->GetSubMenu() ;
58751a86 453 if (subMenu)
d46342af
SC
454 {
455 subMenu->MacBeforeDisplay( true ) ;
456 }
fa4a6942 457 else // normal item
d46342af
SC
458 {
459 #if TARGET_CARBON
6524e8f0
SC
460 // what we do here is to hide the special items which are
461 // shown in the application menu anyhow -- it doesn't make
462 // sense to show them in their normal place as well
463 if ( item->GetId() == wxApp::s_macAboutMenuItemId ||
464 ( UMAGetSystemVersion() >= 0x1000 && (
465 item->GetId() == wxApp::s_macPreferencesMenuItemId ||
466 item->GetId() == wxApp::s_macExitMenuItemId ) ) )
467
d46342af 468 {
6524e8f0
SC
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
476 wxMenuItemList::compatibility_iterator nextNode = node->GetNext();
477 wxMenuItem *next = nextNode ? nextNode->GetData() : NULL;
478
479 size_t posSeptoHide;
480 if ( !previousItem && next && next->IsSeparator() )
d46342af 481 {
6524e8f0
SC
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 }
fa4a6942 504
6524e8f0
SC
505 if ( posSeptoHide )
506 {
507 // hide the separator as well
508 ChangeMenuItemAttributes( MAC_WXHMENU( GetHMenu() ),
509 posSeptoHide,
510 kMenuItemAttrHidden,
511 0 );
d46342af
SC
512 }
513 }
fa4a6942 514 #endif // TARGET_CARBON
d46342af
SC
515 }
516 previousItem = item ;
517 }
518
519 if ( isSubMenu )
520 ::InsertMenu(MAC_WXHMENU( GetHMenu()), -1);
521
522}
523// undo all changes from the MacBeforeDisplay call
58751a86 524void wxMenu::MacAfterDisplay( bool isSubMenu )
d46342af
SC
525{
526 if ( isSubMenu )
527 ::DeleteMenu(MacGetMenuId());
528
529 wxMenuItem* previousItem = NULL ;
530 int pos ;
71f2fb52 531 wxMenuItemList::compatibility_iterator node;
d46342af 532 wxMenuItem *item;
58751a86 533 for (pos = 0, node = GetMenuItems().GetFirst(); node; node = node->GetNext(), pos++)
d46342af
SC
534 {
535 item = (wxMenuItem *)node->GetData();
536 wxMenu* subMenu = item->GetSubMenu() ;
58751a86 537 if (subMenu)
d46342af
SC
538 {
539 subMenu->MacAfterDisplay( true ) ;
540 }
541 else
542 {
543 // no need to undo hidings
544 }
545 previousItem = item ;
546 }
547}
548
e9576ca5 549// Menu Bar
519cb848 550
58751a86 551/*
519cb848
SC
552
553Mac Implementation note :
554
555The Mac has only one global menubar, so we attempt to install the currently
556active menubar from a frame, we currently don't take into account mdi-frames
557which would ask for menu-merging
558
58751a86 559Secondly there is no mac api for changing a menubar that is not the current
519cb848
SC
560menubar, so we have to wait for preparing the actual menubar until the
561wxMenubar is to be used
562
58751a86 563We can in subsequent versions use MacInstallMenuBar to provide some sort of
519cb848
SC
564auto-merge for MDI in case this will be necessary
565
566*/
567
568wxMenuBar* wxMenuBar::s_macInstalledMenuBar = NULL ;
1b1d2207 569wxMenuBar* wxMenuBar::s_macCommonMenuBar = NULL ;
22e3c5bd
SC
570bool wxMenuBar::s_macAutoWindowMenu = true ;
571WXHMENU wxMenuBar::s_macWindowMenuHandle = 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 }
22e3c5bd
SC
780 if ( GetAutoWindowMenu() )
781 {
782 if ( MacGetWindowMenuHMenu() == NULL )
783 {
784 CreateStandardWindowMenu( 0 , (MenuHandle*) &s_macWindowMenuHandle ) ;
785 }
786 InsertMenu( (MenuHandle) MacGetWindowMenuHMenu() , 0 ) ;
787 }
e40298d5
JS
788 ::DrawMenuBar() ;
789 s_macInstalledMenuBar = this;
519cb848
SC
790}
791
e7549107 792void wxMenuBar::EnableTop(size_t pos, bool enable)
e9576ca5 793{
e7549107 794 wxCHECK_RET( IsAttached(), wxT("doesn't work with unattached menubars") );
71f2fb52 795 _wxMenuAt(m_menus, pos)->MacEnableMenu( enable ) ;
e7549107 796 Refresh();
e9576ca5
SC
797}
798
c393c740
JS
799bool wxMenuBar::Enable( bool enable)
800{
5059f192 801 wxCHECK_MSG( IsAttached(), false, wxT("doesn't work with unattached menubars") );
c393c740
JS
802 size_t i;
803 for (i = 0; i < GetMenuCount(); i++)
804 {
805 EnableTop(i, enable);
806 }
807 return true;
808}
809
e7549107 810void wxMenuBar::SetLabelTop(size_t pos, const wxString& label)
e9576ca5 811{
e7549107 812 wxCHECK_RET( pos < GetMenuCount(), wxT("invalid menu index") );
e9576ca5 813
e7549107 814 m_titles[pos] = label;
e9576ca5 815
e7549107
SC
816 if ( !IsAttached() )
817 {
e9576ca5 818 return;
e7549107 819 }
e9576ca5 820
71f2fb52
RN
821 _wxMenuAt(m_menus, pos)->SetTitle( label ) ;
822
e40298d5
JS
823 if (wxMenuBar::s_macInstalledMenuBar == this) // are we currently installed ?
824 {
825 ::SetMenuBar( GetMenuBar() ) ;
826 ::InvalMenuBar() ;
827 }
e9576ca5
SC
828}
829
e7549107 830wxString wxMenuBar::GetLabelTop(size_t pos) const
e9576ca5 831{
e7549107
SC
832 wxCHECK_MSG( pos < GetMenuCount(), wxEmptyString,
833 wxT("invalid menu index in wxMenuBar::GetLabelTop") );
519cb848 834
e7549107 835 return m_titles[pos];
e9576ca5
SC
836}
837
e7549107 838int wxMenuBar::FindMenu(const wxString& title)
e9576ca5 839{
e7549107 840 wxString menuTitle = wxStripMenuCodes(title);
e9576ca5 841
e7549107
SC
842 size_t count = GetMenuCount();
843 for ( size_t i = 0; i < count; i++ )
e9576ca5 844 {
e7549107
SC
845 wxString title = wxStripMenuCodes(m_titles[i]);
846 if ( menuTitle == title )
58751a86 847 return i;
e9576ca5 848 }
e9576ca5 849
e7549107 850 return wxNOT_FOUND;
e9576ca5 851
e9576ca5
SC
852}
853
e7549107 854
e7549107
SC
855// ---------------------------------------------------------------------------
856// wxMenuBar construction
857// ---------------------------------------------------------------------------
858
859wxMenu *wxMenuBar::Replace(size_t pos, wxMenu *menu, const wxString& title)
e9576ca5 860{
e7549107
SC
861 wxMenu *menuOld = wxMenuBarBase::Replace(pos, menu, title);
862 if ( !menuOld )
863 return FALSE;
864 m_titles[pos] = title;
e9576ca5 865
e7549107 866 if ( IsAttached() )
e9576ca5 867 {
e40298d5
JS
868 if (s_macInstalledMenuBar == this)
869 {
d46342af 870 menuOld->MacAfterDisplay( false ) ;
e40298d5
JS
871 ::DeleteMenu( menuOld->MacGetMenuId() /* m_menus[pos]->MacGetMenuId() */ ) ;
872 {
d46342af 873 menu->MacBeforeDisplay( false ) ;
a9412f8f 874 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , title , m_font.GetEncoding() ) ;
e40298d5
JS
875 if ( pos == m_menus.GetCount() - 1)
876 {
877 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , 0 ) ;
878 }
879 else
880 {
71f2fb52 881 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , _wxMenuAt(m_menus, pos+1)->MacGetMenuId() ) ;
e40298d5
JS
882 }
883 }
884 }
e9576ca5 885
e7549107 886 Refresh();
e9576ca5 887 }
0f4c4140
SC
888 if (m_invokingWindow)
889 wxMenubarSetInvokingWindow( menu, m_invokingWindow );
e9576ca5 890
e7549107 891 return menuOld;
e9576ca5
SC
892}
893
e7549107 894bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title)
e9576ca5 895{
e7549107
SC
896 if ( !wxMenuBarBase::Insert(pos, menu, title) )
897 return FALSE;
e9576ca5 898
e7549107 899 m_titles.Insert(title, pos);
e9576ca5 900
a9412f8f 901 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , title , m_font.GetEncoding() ) ;
e7549107 902
d46342af 903 if ( IsAttached() && s_macInstalledMenuBar == this )
e9576ca5 904 {
d46342af 905 if (s_macInstalledMenuBar == this)
e40298d5 906 {
d46342af
SC
907 menu->MacBeforeDisplay( false ) ;
908 if ( pos == (size_t) -1 || pos + 1 == m_menus.GetCount() )
909 {
910 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , 0 ) ;
911 }
912 else
913 {
71f2fb52 914 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , _wxMenuAt(m_menus, pos+1)->MacGetMenuId() ) ;
d46342af 915 }
e40298d5 916 }
e7549107 917 Refresh();
e9576ca5 918 }
0f4c4140
SC
919 if (m_invokingWindow)
920 wxMenubarSetInvokingWindow( menu, m_invokingWindow );
e7549107
SC
921
922 return TRUE;
e9576ca5
SC
923}
924
51abe921
SC
925wxMenu *wxMenuBar::Remove(size_t pos)
926{
927 wxMenu *menu = wxMenuBarBase::Remove(pos);
928 if ( !menu )
929 return NULL;
930
931 if ( IsAttached() )
932 {
e40298d5
JS
933 if (s_macInstalledMenuBar == this)
934 {
935 ::DeleteMenu( menu->MacGetMenuId() /* m_menus[pos]->MacGetMenuId() */ ) ;
936 }
51abe921 937
51abe921
SC
938 Refresh();
939 }
940
5fe38474 941 m_titles.RemoveAt(pos);
51abe921
SC
942
943 return menu;
944}
945
946bool wxMenuBar::Append(wxMenu *menu, const wxString& title)
947{
948 WXHMENU submenu = menu ? menu->GetHMenu() : 0;
949 wxCHECK_MSG( submenu, FALSE, wxT("can't append invalid menu to menubar") );
950
951 if ( !wxMenuBarBase::Append(menu, title) )
952 return FALSE;
953
51abe921 954 m_titles.Add(title);
58751a86 955
a9412f8f 956 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , title , m_font.GetEncoding() ) ;
51abe921
SC
957
958 if ( IsAttached() )
959 {
e40298d5
JS
960 if (s_macInstalledMenuBar == this)
961 {
94c80d52 962 menu->MacBeforeDisplay( false ) ;
e40298d5
JS
963 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , 0 ) ;
964 }
51abe921
SC
965
966 Refresh();
967 }
968
0f4c4140 969 // m_invokingWindow is set after wxFrame::SetMenuBar(). This call enables
7c15086c
SC
970 // adding menu later on.
971 if (m_invokingWindow)
972 wxMenubarSetInvokingWindow( menu, m_invokingWindow );
973
51abe921
SC
974 return TRUE;
975}
976
2b5f62a0
VZ
977static void wxMenubarUnsetInvokingWindow( wxMenu *menu )
978{
979 menu->SetInvokingWindow( (wxWindow*) NULL );
980
71f2fb52 981 wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst();
2b5f62a0
VZ
982 while (node)
983 {
984 wxMenuItem *menuitem = node->GetData();
985 if (menuitem->IsSubMenu())
986 wxMenubarUnsetInvokingWindow( menuitem->GetSubMenu() );
987 node = node->GetNext();
988 }
989}
990
991static void wxMenubarSetInvokingWindow( wxMenu *menu, wxWindow *win )
992{
993 menu->SetInvokingWindow( win );
994
71f2fb52 995 wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst();
2b5f62a0
VZ
996 while (node)
997 {
998 wxMenuItem *menuitem = node->GetData();
999 if (menuitem->IsSubMenu())
1000 wxMenubarSetInvokingWindow( menuitem->GetSubMenu() , win );
1001 node = node->GetNext();
1002 }
1003}
1004
1005void wxMenuBar::UnsetInvokingWindow()
1006{
7c15086c 1007 m_invokingWindow = (wxWindow*) NULL;
71f2fb52 1008 wxMenuList::compatibility_iterator node = m_menus.GetFirst();
2b5f62a0
VZ
1009 while (node)
1010 {
1011 wxMenu *menu = node->GetData();
1012 wxMenubarUnsetInvokingWindow( menu );
1013 node = node->GetNext();
1014 }
1015}
1016
1017void wxMenuBar::SetInvokingWindow(wxFrame *frame)
1018{
7c15086c 1019 m_invokingWindow = frame;
71f2fb52 1020 wxMenuList::compatibility_iterator node = m_menus.GetFirst();
2b5f62a0
VZ
1021 while (node)
1022 {
1023 wxMenu *menu = node->GetData();
1024 wxMenubarSetInvokingWindow( menu, frame );
1025 node = node->GetNext();
1026 }
1027}
1028
90b959ae 1029void wxMenuBar::Detach()
2f1ae414 1030{
90b959ae
SC
1031 wxMenuBarBase::Detach() ;
1032}
2f1ae414 1033
90b959ae
SC
1034void wxMenuBar::Attach(wxFrame *frame)
1035{
1036 wxMenuBarBase::Attach( frame ) ;
2f1ae414 1037}
51abe921
SC
1038// ---------------------------------------------------------------------------
1039// wxMenuBar searching for menu items
1040// ---------------------------------------------------------------------------
1041
1042// Find the itemString in menuString, and return the item id or wxNOT_FOUND
1043int wxMenuBar::FindMenuItem(const wxString& menuString,
1044 const wxString& itemString) const
1045{
1046 wxString menuLabel = wxStripMenuCodes(menuString);
1047 size_t count = GetMenuCount();
1048 for ( size_t i = 0; i < count; i++ )
1049 {
1050 wxString title = wxStripMenuCodes(m_titles[i]);
ef089805 1051 if ( menuLabel == title )
71f2fb52 1052 return _wxMenuAt(m_menus, i)->FindItem(itemString);
51abe921
SC
1053 }
1054
1055 return wxNOT_FOUND;
1056}
1057
1058wxMenuItem *wxMenuBar::FindItem(int id, wxMenu **itemMenu) const
1059{
1060 if ( itemMenu )
1061 *itemMenu = NULL;
1062
1063 wxMenuItem *item = NULL;
1064 size_t count = GetMenuCount();
1065 for ( size_t i = 0; !item && (i < count); i++ )
1066 {
71f2fb52 1067 item = _wxMenuAt(m_menus, i)->FindItem(id, itemMenu);
51abe921
SC
1068 }
1069
1070 return item;
1071}
1072
1073