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