]> git.saurik.com Git - wxWidgets.git/blame - src/osx/menu_osx.cpp
fix building with WXWIN_COMPATIBILITY_2_8 == 0
[wxWidgets.git] / src / osx / menu_osx.cpp
CommitLineData
524c47aa
SC
1/////////////////////////////////////////////////////////////////////////////
2// Name: src/osx/menu_osx.cpp
3// Purpose: wxMenu, wxMenuBar, wxMenuItem
4// Author: Stefan Csomor
5// Modified by:
6// Created: 1998-01-01
524c47aa
SC
7// Copyright: (c) Stefan Csomor
8// Licence: wxWindows licence
9/////////////////////////////////////////////////////////////////////////////
10
11// ============================================================================
12// headers & declarations
13// ============================================================================
14
15// wxWidgets headers
16// -----------------
17
18#include "wx/wxprec.h"
19
11fed901
SC
20#if wxUSE_MENUS
21
524c47aa
SC
22#include "wx/menu.h"
23
24#ifndef WX_PRECOMP
25 #include "wx/log.h"
26 #include "wx/app.h"
27 #include "wx/utils.h"
28 #include "wx/frame.h"
42a81643 29 #include "wx/dialog.h"
524c47aa
SC
30 #include "wx/menuitem.h"
31#endif
32
33#include "wx/osx/private.h"
34
35// other standard headers
36// ----------------------
37#include <string.h>
38
39IMPLEMENT_ABSTRACT_CLASS( wxMenuImpl , wxObject )
40
41wxMenuImpl::~wxMenuImpl()
42{
43}
44
c1ec7ee8 45// the (popup) menu title has this special menuid
524c47aa
SC
46static const int idMenuTitle = -3;
47
48// ============================================================================
49// implementation
50// ============================================================================
524c47aa
SC
51
52// Menus
53
54// Construct a menu with optional title (then use append)
55
524c47aa
SC
56void wxMenu::Init()
57{
58 m_doBreak = false;
524c47aa
SC
59 m_allowRearrange = true;
60 m_noEventsMode = false;
03647350 61
524c47aa
SC
62 m_peer = wxMenuImpl::Create( this, wxStripMenuCodes(m_title) );
63
64
65 // if we have a title, insert it in the beginning of the menu
66 if ( !m_title.empty() )
67 {
68 Append(idMenuTitle, m_title) ;
69 AppendSeparator() ;
70 }
71}
72
73wxMenu::~wxMenu()
74{
75 delete m_peer;
76}
77
78WXHMENU wxMenu::GetHMenu() const
79{
80 if ( m_peer )
81 return m_peer->GetHMenu();
82 return NULL;
83}
84
85void wxMenu::Break()
86{
87 // not available on the mac platform
88}
89
524c47aa
SC
90void wxMenu::SetAllowRearrange( bool allow )
91{
92 m_allowRearrange = allow;
93}
94
95void wxMenu::SetNoEventsMode( bool noEvents )
96{
97 m_noEventsMode = noEvents;
98}
99
100// function appends a new item or submenu to the menu
101// append a new item or submenu to the menu
98835830 102bool wxMenu::DoInsertOrAppend(wxMenuItem *item, size_t pos)
524c47aa 103{
98835830
VZ
104 wxASSERT_MSG( item != NULL, wxT("can't append NULL item to the menu") );
105 GetPeer()->InsertOrAppend( item, pos );
524c47aa 106
27d79a50
VZ
107 bool check = false;
108
98835830 109 if ( item->IsSeparator() )
524c47aa
SC
110 {
111 // nothing to do here
112 }
113 else
114 {
98835830 115 wxMenu *pSubMenu = item->GetSubMenu() ;
524c47aa
SC
116 if ( pSubMenu != NULL )
117 {
118 wxASSERT_MSG( pSubMenu->GetHMenu() != NULL , wxT("invalid submenu added"));
119 pSubMenu->m_menuParent = this ;
120
121 pSubMenu->DoRearrange();
122 }
27d79a50 123 else if ( item->IsRadio() )
03647350 124 {
27d79a50
VZ
125 // If a previous or next item is a radio button, add this radio
126 // button to the existing radio group. Otherwise start a new one
127 // for it.
128 wxMenuItemList& items = GetMenuItems();
524c47aa 129
27d79a50
VZ
130 size_t const
131 posItem = pos == (size_t)-1 ? items.GetCount() - 1 : pos;
524c47aa 132
27d79a50
VZ
133 wxMenuItemList::compatibility_iterator node = items.Item(posItem);
134 wxCHECK_MSG( node, false, wxS("New item must have been inserted") );
524c47aa 135
27d79a50
VZ
136 bool foundGroup = false;
137 if ( node->GetPrevious() )
138 {
139 wxMenuItem* const prev = node->GetPrevious()->GetData();
140
141 if ( prev->IsRadio() )
142 {
143 // This item is in the same group as the preceding one so
144 // we should use the same starting item, but getting it is
145 // a bit difficult as we can't query the start radio group
146 // item for it.
147 const int groupStart = prev->IsRadioGroupStart()
148 ? posItem - 1
149 : prev->GetRadioGroupStart();
150 item->SetRadioGroupStart(groupStart);
151
152 // We must also account for the new item by incrementing
153 // the index of the last item in this group.
154 wxMenuItem* const first = items.Item(groupStart)->GetData();
155 first->SetRadioGroupEnd(first->GetRadioGroupEnd() + 1);
156
157 foundGroup = true;
158 }
159 }
524c47aa 160
27d79a50
VZ
161 if ( !foundGroup && node->GetNext() )
162 {
163 wxMenuItem* const next = node->GetNext()->GetData();
524c47aa 164
27d79a50
VZ
165 if ( next->IsRadio() )
166 {
167 // This item is the new starting item of this group as the
168 // previous item is not a radio item.
169 wxASSERT_MSG( next->IsRadioGroupStart(),
170 wxS("Where is the start of this group?") );
524c47aa 171
27d79a50
VZ
172 // The index of the last item of the radio group must be
173 // incremented to account for the new item.
174 item->SetAsRadioGroupStart();
175 item->SetRadioGroupEnd(next->GetRadioGroupEnd() + 1);
524c47aa 176
27d79a50
VZ
177 // And the previous start item is not one any longer.
178 next->SetAsRadioGroupStart(false);
3b28ca65 179
27d79a50
VZ
180 foundGroup = true;
181 }
182 }
524c47aa 183
27d79a50 184 if ( !foundGroup )
524c47aa 185 {
27d79a50
VZ
186 // start a new radio group
187 item->SetAsRadioGroupStart();
188 item->SetRadioGroupEnd(posItem);
189
190 // ensure that we have a checked item in the radio group
191 check = true;
524c47aa 192 }
27d79a50
VZ
193 }
194 else
195 {
196 if ( item->GetId() == idMenuTitle )
197 item->GetMenu()->Enable( idMenuTitle, false );
198 }
199 }
200
201 // We also need to update the indices of radio group start and end we store
202 // in any existing radio items after this item.
203 if ( pos < GetMenuItemCount() - 1 ) // takes into account pos == -1 case
204 {
205 for ( wxMenuItemList::compatibility_iterator
206 node = GetMenuItems().Item(pos + 1);
207 node;
208 node = node->GetNext() )
209 {
210 wxMenuItem* const item = node->GetData();
211 if ( item->IsRadio() )
524c47aa 212 {
27d79a50
VZ
213 if ( item->IsRadioGroupStart() )
214 {
215 // If the starting item is after the just inserted one,
216 // then the end one must be after it too and needs to be
217 // updated.
218 item->SetRadioGroupEnd(item->GetRadioGroupEnd() + 1);
219 }
220 else // Not the first radio group item.
221 {
222 // We need to update the start item index only if it is
223 // after the just inserted item.
224 const int groupStart = item->GetRadioGroupStart();
225 if ( (size_t)groupStart > pos )
226 item->SetRadioGroupStart(groupStart + 1);
227 }
524c47aa
SC
228 }
229 }
230 }
524c47aa 231
27d79a50
VZ
232 // if we're already attached to the menubar, we must update it
233 if ( IsAttached() && GetMenuBar()->IsAttached() )
234 GetMenuBar()->Refresh();
524c47aa
SC
235
236 if ( check )
524c47aa
SC
237 item->Check(true);
238
27d79a50
VZ
239 return true ;
240}
241
242wxMenuItem* wxMenu::DoAppend(wxMenuItem *item)
243{
244 if (wxMenuBase::DoAppend(item) && DoInsertOrAppend(item) )
245 return item;
246
247 return NULL;
524c47aa
SC
248}
249
250wxMenuItem* wxMenu::DoInsert(size_t pos, wxMenuItem *item)
251{
252 if (wxMenuBase::DoInsert(pos, item) && DoInsertOrAppend(item, pos))
253 return item;
254
255 return NULL;
256}
257
258wxMenuItem *wxMenu::DoRemove(wxMenuItem *item)
259{
3b28ca65 260 if ( item->IsRadio() )
09fbcf2a
VZ
261 {
262 // Check if we're removing the item starting the radio group
3b28ca65 263 if ( item->IsRadioGroupStart() )
09fbcf2a 264 {
3b28ca65
VZ
265 // Yes, we do, update the next radio group item, if any, to be the
266 // start one now.
267 const int endGroup = item->GetRadioGroupEnd();
268
269 wxMenuItemList::compatibility_iterator
270 node = GetMenuItems().Item(endGroup);
271 wxASSERT_MSG( node, wxS("Should have valid radio group end") );
272
273 while ( node->GetData() != item )
274 {
275 const wxMenuItemList::compatibility_iterator
276 prevNode = node->GetPrevious();
277 wxMenuItem* const prevItem = prevNode->GetData();
278 if ( prevItem == item )
279 {
280 prevItem->SetAsRadioGroupStart();
281 prevItem->SetRadioGroupEnd(endGroup);
282 break;
283 }
284
285 node = prevNode;
286 }
09fbcf2a
VZ
287 }
288 }
289
524c47aa
SC
290/*
291 // we need to find the items position in the child list
292 size_t pos;
293 wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
294
295 for ( pos = 0; node; pos++ )
296 {
297 if ( node->GetData() == item )
298 break;
299
300 node = node->GetNext();
301 }
302
303 // DoRemove() (unlike Remove) can only be called for existing item!
304 wxCHECK_MSG( node, NULL, wxT("bug in wxMenu::Remove logic") );
305
306 wxOSXMenuRemoveItem(m_hMenu , pos );
307 */
22756322 308 GetPeer()->Remove( item );
524c47aa
SC
309 // and from internal data structures
310 return wxMenuBase::DoRemove(item);
311}
312
313void wxMenu::SetTitle(const wxString& label)
314{
315 m_title = label ;
22756322 316 GetPeer()->SetTitle( wxStripMenuCodes( label ) );
524c47aa
SC
317}
318
319bool wxMenu::ProcessCommand(wxCommandEvent & event)
320{
321 bool processed = false;
322
323 // Try the menu's event handler
324 if ( /* !processed && */ GetEventHandler())
325 processed = GetEventHandler()->SafelyProcessEvent(event);
326
327 // Try the window the menu was popped up from
328 // (and up through the hierarchy)
6a57bd93 329 wxWindow *win = GetWindow();
524c47aa
SC
330 if ( !processed && win )
331 processed = win->HandleWindowEvent(event);
332
333 return processed;
334}
335
336// ---------------------------------------------------------------------------
337// other
338// ---------------------------------------------------------------------------
339
524c47aa
SC
340// MacOS needs to know about submenus somewhere within this menu
341// before it can be displayed, also hide special menu items
342// like preferences that are handled by the OS
343void wxMenu::DoRearrange()
344{
345 if ( !AllowRearrange() )
346 return;
03647350 347
524c47aa
SC
348 wxMenuItem* previousItem = NULL ;
349 size_t pos ;
350 wxMenuItemList::compatibility_iterator node;
351 wxMenuItem *item;
352
353 for (pos = 0, node = GetMenuItems().GetFirst(); node; node = node->GetNext(), pos++)
354 {
355 item = (wxMenuItem *)node->GetData();
356 wxMenu* subMenu = item->GetSubMenu() ;
357 if (subMenu)
358 {
359 // already done
360 }
361 else // normal item
362 {
363 // what we do here is to hide the special items which are
364 // shown in the application menu anyhow -- it doesn't make
365 // sense to show them in their normal place as well
366 if ( item->GetId() == wxApp::s_macAboutMenuItemId ||
367 item->GetId() == wxApp::s_macPreferencesMenuItemId ||
368 item->GetId() == wxApp::s_macExitMenuItemId )
369
370 {
371 item->GetPeer()->Hide( true );
372
373 // also check for a separator which was used just to
374 // separate this item from the others, so don't leave
375 // separator at the menu start or end nor 2 consecutive
376 // separators
377 wxMenuItemList::compatibility_iterator nextNode = node->GetNext();
378 wxMenuItem *next = nextNode ? nextNode->GetData() : NULL;
379
380 wxMenuItem *sepToHide = 0;
381 if ( !previousItem && next && next->IsSeparator() )
382 {
383 // next (i.e. second as we must be first) item is
384 // the separator to hide
9a83f860 385 wxASSERT_MSG( pos == 0, wxT("should be the menu start") );
524c47aa
SC
386 sepToHide = next;
387 }
388 else if ( GetMenuItems().GetCount() == pos + 1 &&
389 previousItem != NULL &&
390 previousItem->IsSeparator() )
391 {
392 // prev item is a trailing separator we want to hide
393 sepToHide = previousItem;
394 }
395 else if ( previousItem && previousItem->IsSeparator() &&
396 next && next->IsSeparator() )
397 {
398 // two consecutive separators, this is one too many
399 sepToHide = next;
400 }
401
402 if ( sepToHide )
403 {
404 // hide the separator as well
405 sepToHide->GetPeer()->Hide( true );
406 }
407 }
408 }
409
410 previousItem = item ;
411 }
412}
413
414
415bool wxMenu::HandleCommandUpdateStatus( wxMenuItem* item, wxWindow* senderWindow )
416{
c1ec7ee8
SC
417 int menuid = item ? item->GetId() : 0;
418 wxUpdateUIEvent event(menuid);
524c47aa
SC
419 event.SetEventObject( this );
420
421 bool processed = false;
422
423 // Try the menu's event handler
424 {
425 wxEvtHandler *handler = GetEventHandler();
426 if ( handler )
427 processed = handler->ProcessEvent(event);
428 }
429
430 // Try the window the menu was popped up from
431 // (and up through the hierarchy)
432 if ( !processed )
433 {
6a57bd93
VZ
434 wxWindow *win = GetWindow();
435 if ( win )
436 processed = win->HandleWindowEvent(event);
524c47aa
SC
437 }
438
439 if ( !processed && senderWindow != NULL)
440 {
441 processed = senderWindow->HandleWindowEvent(event);
442 }
443
444 if ( processed )
445 {
446 // if anything changed, update the changed attribute
447 if (event.GetSetText())
c1ec7ee8 448 SetLabel(menuid, event.GetText());
524c47aa 449 if (event.GetSetChecked())
c1ec7ee8 450 Check(menuid, event.GetChecked());
524c47aa 451 if (event.GetSetEnabled())
c1ec7ee8 452 Enable(menuid, event.GetEnabled());
524c47aa 453 }
6a57bd93 454 else
eb68a54a
SC
455 {
456#if wxOSX_USE_CARBON
457 // these two items are also managed by the Carbon Menu Manager, therefore we must
458 // always reset them ourselves
459 UInt32 cmd = 0;
6a57bd93 460
c1ec7ee8 461 if ( menuid == wxApp::s_macExitMenuItemId )
eb68a54a
SC
462 {
463 cmd = kHICommandQuit;
464 }
c1ec7ee8 465 else if (menuid == wxApp::s_macPreferencesMenuItemId )
eb68a54a
SC
466 {
467 cmd = kHICommandPreferences;
468 }
6a57bd93 469
eb68a54a
SC
470 if ( cmd != 0 )
471 {
472 if ( !item->IsEnabled() || wxDialog::OSXHasModalDialogsOpen() )
473 DisableMenuCommand( NULL , cmd ) ;
474 else
475 EnableMenuCommand( NULL , cmd ) ;
6a57bd93 476
eb68a54a
SC
477 }
478#endif
479 }
480
524c47aa
SC
481 return processed;
482}
483
484bool wxMenu::HandleCommandProcess( wxMenuItem* item, wxWindow* senderWindow )
485{
c1ec7ee8 486 int menuid = item ? item->GetId() : 0;
524c47aa
SC
487 bool processed = false;
488 if (item->IsCheckable())
489 item->Check( !item->IsChecked() ) ;
490
c1ec7ee8 491 if ( SendEvent( menuid , item->IsCheckable() ? item->IsChecked() : -1 ) )
524c47aa
SC
492 processed = true ;
493 else
494 {
495 if ( senderWindow != NULL )
496 {
ce7fe42e 497 wxCommandEvent event(wxEVT_MENU , menuid);
4815db04 498 event.SetEventObject(this);
524c47aa
SC
499 event.SetInt(item->IsCheckable() ? item->IsChecked() : -1);
500
501 if ( senderWindow->HandleWindowEvent(event) )
502 processed = true ;
503 }
504 }
c46d0503
SC
505
506 if(!processed && item)
507 {
508 processed = item->GetPeer()->DoDefault();
509 }
510
524c47aa
SC
511 return processed;
512}
513
514void wxMenu::HandleMenuItemHighlighted( wxMenuItem* item )
515{
c1ec7ee8
SC
516 int menuid = item ? item->GetId() : 0;
517 wxMenuEvent wxevent(wxEVT_MENU_HIGHLIGHT, menuid, this);
524c47aa
SC
518 DoHandleMenuEvent( wxevent );
519}
520
7f3f059a 521void wxMenu::DoHandleMenuOpenedOrClosed(wxEventType evtType)
524c47aa 522{
7f3f059a
VZ
523 // Popup menu being currently shown or NULL, defined in wincmn.cpp.
524 extern wxMenu *wxCurrentPopupMenu;
525
526 // Set the id to allow wxMenuEvent::IsPopup() to work correctly.
527 int menuid = this == wxCurrentPopupMenu ? wxID_ANY : 0;
528 wxMenuEvent wxevent(evtType, menuid, this);
524c47aa
SC
529 DoHandleMenuEvent( wxevent );
530}
531
7f3f059a
VZ
532void wxMenu::HandleMenuOpened()
533{
534 DoHandleMenuOpenedOrClosed(wxEVT_MENU_OPEN);
535}
536
524c47aa
SC
537void wxMenu::HandleMenuClosed()
538{
7f3f059a 539 DoHandleMenuOpenedOrClosed(wxEVT_MENU_CLOSE);
524c47aa
SC
540}
541
542bool wxMenu::DoHandleMenuEvent(wxEvent& wxevent)
543{
544 wxevent.SetEventObject(this);
545 wxEvtHandler* handler = GetEventHandler();
546 if (handler && handler->ProcessEvent(wxevent))
547 {
548 return true;
549 }
550 else
551 {
6a57bd93 552 wxWindow *win = GetWindow();
524c47aa
SC
553 if (win)
554 {
555 if ( win->HandleWindowEvent(wxevent) )
556 return true;
557 }
558 }
559 return false;
560}
561
562// Menu Bar
563
564/*
565
566Mac Implementation note :
567
568The Mac has only one global menubar, so we attempt to install the currently
569active menubar from a frame, we currently don't take into account mdi-frames
570which would ask for menu-merging
571
572Secondly there is no mac api for changing a menubar that is not the current
573menubar, so we have to wait for preparing the actual menubar until the
574wxMenubar is to be used
575
576We can in subsequent versions use MacInstallMenuBar to provide some sort of
577auto-merge for MDI in case this will be necessary
578
579*/
580
581wxMenuBar* wxMenuBar::s_macInstalledMenuBar = NULL ;
582wxMenuBar* wxMenuBar::s_macCommonMenuBar = NULL ;
583bool wxMenuBar::s_macAutoWindowMenu = true ;
584WXHMENU wxMenuBar::s_macWindowMenuHandle = NULL ;
585
3d184f1e
SC
586
587wxMenu* emptyMenuBar = NULL;
588
8ceae028
SC
589const int firstMenuPos = 1; // to account for the 0th application menu on mac
590
524c47aa
SC
591void wxMenuBar::Init()
592{
3d184f1e
SC
593 if ( emptyMenuBar == NULL )
594 {
595 emptyMenuBar = new wxMenu();
596
597 wxMenu* appleMenu = new wxMenu();
598 appleMenu->SetAllowRearrange(false);
599#if !wxOSX_USE_CARBON
600 // standard menu items, handled in wxMenu::HandleCommandProcess(), see above:
601 wxString hideLabel;
602 hideLabel = wxString::Format(_("Hide %s"), wxTheApp ? wxTheApp->GetAppDisplayName() : _("Application"));
603 appleMenu->Append( wxID_OSX_HIDE, hideLabel + "\tCtrl+H" );
604 appleMenu->Append( wxID_OSX_HIDEOTHERS, _("Hide Others")+"\tAlt+Ctrl+H" );
605 appleMenu->Append( wxID_OSX_SHOWALL, _("Show All") );
606 appleMenu->AppendSeparator();
607
608 // Do always add "Quit" item unconditionally however, it can't be disabled.
609 wxString quitLabel;
610 quitLabel = wxString::Format(_("Quit %s"), wxTheApp ? wxTheApp->GetAppDisplayName() : _("Application"));
611 appleMenu->Append( wxApp::s_macExitMenuItemId, quitLabel + "\tCtrl+Q" );
612#endif // !wxOSX_USE_CARBON
613
614 emptyMenuBar->AppendSubMenu(appleMenu, "\x14") ;
615 }
616
524c47aa
SC
617 m_eventHandler = this;
618 m_menuBarFrame = NULL;
524c47aa 619 m_rootMenu = new wxMenu();
6a57bd93
VZ
620 m_rootMenu->Attach(this);
621
1ea5ef01
SC
622 m_appleMenu = new wxMenu();
623 m_appleMenu->SetAllowRearrange(false);
4f1b95ea
VZ
624
625 // Create standard items unless the application explicitly disabled this by
626 // setting the corresponding ids to wxID_NONE: although this is not
627 // recommended, sometimes these items really don't make sense.
628 if ( wxApp::s_macAboutMenuItemId != wxID_NONE )
629 {
c46d0503 630 wxString aboutLabel(_("About"));
c8bef6e3
VZ
631 if ( wxTheApp )
632 aboutLabel << ' ' << wxTheApp->GetAppDisplayName();
633 else
634 aboutLabel << "...";
635 m_appleMenu->Append( wxApp::s_macAboutMenuItemId, aboutLabel);
4f1b95ea
VZ
636 m_appleMenu->AppendSeparator();
637 }
638
1ea5ef01 639#if !wxOSX_USE_CARBON
4f1b95ea
VZ
640 if ( wxApp::s_macPreferencesMenuItemId != wxID_NONE )
641 {
86646f2a 642 m_appleMenu->Append( wxApp::s_macPreferencesMenuItemId,
c46d0503 643 _("Preferences...") + "\tCtrl+," );
4f1b95ea
VZ
644 m_appleMenu->AppendSeparator();
645 }
646
c46d0503 647 // standard menu items, handled in wxMenu::HandleCommandProcess(), see above:
48d43515
SC
648 wxString hideLabel;
649 hideLabel = wxString::Format(_("Hide %s"), wxTheApp ? wxTheApp->GetAppDisplayName() : _("Application"));
650 m_appleMenu->Append( wxID_OSX_HIDE, hideLabel + "\tCtrl+H" );
c46d0503
SC
651 m_appleMenu->Append( wxID_OSX_HIDEOTHERS, _("Hide Others")+"\tAlt+Ctrl+H" );
652 m_appleMenu->Append( wxID_OSX_SHOWALL, _("Show All") );
653 m_appleMenu->AppendSeparator();
654
4f1b95ea 655 // Do always add "Quit" item unconditionally however, it can't be disabled.
48d43515
SC
656 wxString quitLabel;
657 quitLabel = wxString::Format(_("Quit %s"), wxTheApp ? wxTheApp->GetAppDisplayName() : _("Application"));
658 m_appleMenu->Append( wxApp::s_macExitMenuItemId, quitLabel + "\tCtrl+Q" );
4f1b95ea 659#endif // !wxOSX_USE_CARBON
524c47aa 660
1ea5ef01 661 m_rootMenu->AppendSubMenu(m_appleMenu, "\x14") ;
524c47aa
SC
662}
663
664wxMenuBar::wxMenuBar()
665{
666 Init();
667}
668
669wxMenuBar::wxMenuBar( long WXUNUSED(style) )
670{
671 Init();
672}
673
674wxMenuBar::wxMenuBar(size_t count, wxMenu *menus[], const wxString titles[], long WXUNUSED(style))
675{
676 Init();
677
524c47aa
SC
678 for ( size_t i = 0; i < count; i++ )
679 {
680 m_menus.Append(menus[i]);
524c47aa
SC
681
682 menus[i]->Attach(this);
683 Append( menus[i], titles[i] );
684 }
685}
686
687wxMenuBar::~wxMenuBar()
688{
689 if (s_macCommonMenuBar == this)
690 s_macCommonMenuBar = NULL;
691
692 if (s_macInstalledMenuBar == this)
693 {
3d184f1e 694 emptyMenuBar->GetPeer()->MakeRoot();
524c47aa
SC
695 s_macInstalledMenuBar = NULL;
696 }
3d184f1e
SC
697 wxDELETE( m_rootMenu );
698 // apple menu is a submenu, therefore we don't have to delete it
699 m_appleMenu = NULL;
700
701 // deleting the root menu also removes all its wxMenu* submenus, therefore
702 // we must avoid double deleting them in the superclass destructor
703 m_menus.clear();
524c47aa
SC
704}
705
706void wxMenuBar::Refresh(bool WXUNUSED(eraseBackground), const wxRect *WXUNUSED(rect))
707{
708 wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") );
709}
710
711void wxMenuBar::MacInstallMenuBar()
712{
713 if ( s_macInstalledMenuBar == this )
714 return ;
03647350 715
524c47aa 716 m_rootMenu->GetPeer()->MakeRoot();
cc5fe8d4
SC
717
718 // hide items in the apple menu that don't exist in the wx menubar
719
cc5fe8d4 720 wxMenuItem* appleItem = NULL;
aa3e3741 721 wxMenuItem* wxItem = NULL;
eb68a54a 722
e708cada 723 int menuid = wxApp::s_macAboutMenuItemId;
c1ec7ee8
SC
724 appleItem = m_appleMenu->FindItem(menuid);
725 wxItem = FindItem(menuid);
cc5fe8d4 726 if ( appleItem != NULL )
aa3e3741
SC
727 {
728 if ( wxItem == NULL )
729 appleItem->GetPeer()->Hide();
730 else
731 appleItem->SetItemLabel(wxItem->GetItemLabel());
732 }
cc5fe8d4 733
c1ec7ee8
SC
734 menuid = wxApp::s_macPreferencesMenuItemId;
735 appleItem = m_appleMenu->FindItem(menuid);
736 wxItem = FindItem(menuid);
cc5fe8d4 737 if ( appleItem != NULL )
aa3e3741
SC
738 {
739 if ( wxItem == NULL )
740 appleItem->GetPeer()->Hide();
741 else
742 appleItem->SetItemLabel(wxItem->GetItemLabel());
743 }
cc5fe8d4
SC
744
745
524c47aa
SC
746#if 0
747
524c47aa
SC
748 // if we have a mac help menu, clean it up before adding new items
749 MenuHandle helpMenuHandle ;
750 MenuItemIndex firstUserHelpMenuItem ;
751
752 if ( UMAGetHelpMenuDontCreate( &helpMenuHandle , &firstUserHelpMenuItem) == noErr )
753 {
754 for ( int i = CountMenuItems( helpMenuHandle ) ; i >= firstUserHelpMenuItem ; --i )
755 DeleteMenuItem( helpMenuHandle , i ) ;
756 }
757 else
758 {
759 helpMenuHandle = NULL ;
760 }
761
762 if ( wxApp::s_macPreferencesMenuItemId)
763 {
764 wxMenuItem *item = FindItem( wxApp::s_macPreferencesMenuItemId , NULL ) ;
765 if ( item == NULL || !(item->IsEnabled()) )
766 DisableMenuCommand( NULL , kHICommandPreferences ) ;
767 else
768 EnableMenuCommand( NULL , kHICommandPreferences ) ;
769 }
770
771 // Unlike preferences which may or may not exist, the Quit item should be always
772 // enabled unless it is added by the application and then disabled, otherwise
773 // a program would be required to add an item with wxID_EXIT in order to get the
774 // Quit menu item to be enabled, which seems a bit burdensome.
775 if ( wxApp::s_macExitMenuItemId)
776 {
777 wxMenuItem *item = FindItem( wxApp::s_macExitMenuItemId , NULL ) ;
778 if ( item != NULL && !(item->IsEnabled()) )
779 DisableMenuCommand( NULL , kHICommandQuit ) ;
780 else
781 EnableMenuCommand( NULL , kHICommandQuit ) ;
782 }
783
784 wxString strippedHelpMenuTitle = wxStripMenuCodes( wxApp::s_macHelpMenuTitleName ) ;
785 wxString strippedTranslatedHelpMenuTitle = wxStripMenuCodes( wxString( _("&Help") ) ) ;
786 wxMenuList::compatibility_iterator menuIter = m_menus.GetFirst();
787 for (size_t i = 0; i < m_menus.GetCount(); i++, menuIter = menuIter->GetNext())
788 {
789 wxMenuItemList::compatibility_iterator node;
790 wxMenuItem *item;
791 wxMenu* menu = menuIter->GetData() , *subMenu = NULL ;
792 wxString strippedMenuTitle = wxStripMenuCodes(m_titles[i]);
793
794 if ( strippedMenuTitle == wxT("?") || strippedMenuTitle == strippedHelpMenuTitle || strippedMenuTitle == strippedTranslatedHelpMenuTitle )
795 {
796 for (node = menu->GetMenuItems().GetFirst(); node; node = node->GetNext())
797 {
798 item = (wxMenuItem *)node->GetData();
799 subMenu = item->GetSubMenu() ;
800 if (subMenu)
801 {
e8fdf364
JS
802 UMAAppendMenuItem(mh, wxStripMenuCodes(item->GetText()) , wxFont::GetDefaultEncoding() );
803 MenuItemIndex position = CountMenuItems(mh);
804 ::SetMenuItemHierarchicalMenu(mh, position, MAC_WXHMENU(subMenu->GetHMenu()));
524c47aa
SC
805 }
806 else
807 {
808 if ( item->GetId() != wxApp::s_macAboutMenuItemId )
809 {
810 // we have found a user help menu and an item other than the about item,
811 // so we can create the mac help menu now, if we haven't created it yet
812 if ( helpMenuHandle == NULL )
813 {
814 if ( UMAGetHelpMenu( &helpMenuHandle , &firstUserHelpMenuItem) != noErr )
815 {
816 helpMenuHandle = NULL ;
817 break ;
818 }
819 }
820 }
821
822 if ( item->IsSeparator() )
823 {
824 if ( helpMenuHandle )
825 AppendMenuItemTextWithCFString( helpMenuHandle,
826 CFSTR(""), kMenuItemAttrSeparator, 0,NULL);
827 }
828 else
829 {
830 wxAcceleratorEntry*
831 entry = wxAcceleratorEntry::Create( item->GetItemLabel() ) ;
832
833 if ( item->GetId() == wxApp::s_macAboutMenuItemId )
834 {
835 // this will be taken care of below
836 }
837 else
838 {
839 if ( helpMenuHandle )
840 {
841 UMAAppendMenuItem(helpMenuHandle, wxStripMenuCodes(item->GetItemLabel()) , wxFont::GetDefaultEncoding(), entry);
842 SetMenuItemCommandID( helpMenuHandle , CountMenuItems(helpMenuHandle) , wxIdToMacCommand ( item->GetId() ) ) ;
843 SetMenuItemRefCon( helpMenuHandle , CountMenuItems(helpMenuHandle) , (URefCon) item ) ;
844 }
845 }
846
847 delete entry ;
848 }
849 }
850 }
851 }
852
853 else if ( ( m_titles[i] == wxT("Window") || m_titles[i] == wxT("&Window") )
854 && GetAutoWindowMenu() )
855 {
856 if ( MacGetWindowMenuHMenu() == NULL )
857 {
858 CreateStandardWindowMenu( 0 , (MenuHandle*) &s_macWindowMenuHandle ) ;
859 }
860
861 MenuRef wm = (MenuRef)MacGetWindowMenuHMenu();
862 if ( wm == NULL )
863 break;
864
865 // get the insertion point in the standard menu
866 MenuItemIndex winListStart;
867 GetIndMenuItemWithCommandID(wm,
868 kHICommandWindowListSeparator, 1, NULL, &winListStart);
869
870 // add a separator so that the standard items and the custom items
871 // aren't mixed together, but only if this is the first run
872 OSStatus err = GetIndMenuItemWithCommandID(wm,
873 'WXWM', 1, NULL, NULL);
874
875 if ( err == menuItemNotFoundErr )
876 {
877 InsertMenuItemTextWithCFString( wm,
878 CFSTR(""), winListStart-1, kMenuItemAttrSeparator, 'WXWM');
879 }
880
881 wxInsertMenuItemsInMenu(menu, wm, winListStart);
882 }
883 else
884 {
885 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , m_titles[i], GetFont().GetEncoding() ) ;
886 menu->MacBeforeDisplay(false) ;
887
8a145966 888 ::InsertMenu(MAC_WXHMENU(GetMenu(i)->GetHMenu()), 0);
524c47aa
SC
889 }
890 }
891
892 // take care of the about menu item wherever it is
893 {
894 wxMenu* aboutMenu ;
895 wxMenuItem *aboutMenuItem = FindItem(wxApp::s_macAboutMenuItemId , &aboutMenu) ;
896 if ( aboutMenuItem )
897 {
898 wxAcceleratorEntry*
899 entry = wxAcceleratorEntry::Create( aboutMenuItem->GetItemLabel() ) ;
900 UMASetMenuItemText( GetMenuHandle( kwxMacAppleMenuId ) , 1 , wxStripMenuCodes ( aboutMenuItem->GetItemLabel() ) , wxFont::GetDefaultEncoding() );
901 UMAEnableMenuItem( GetMenuHandle( kwxMacAppleMenuId ) , 1 , true );
902 SetMenuItemCommandID( GetMenuHandle( kwxMacAppleMenuId ) , 1 , kHICommandAbout ) ;
903 SetMenuItemRefCon(GetMenuHandle( kwxMacAppleMenuId ) , 1 , (URefCon)aboutMenuItem ) ;
904 UMASetMenuItemShortcut( GetMenuHandle( kwxMacAppleMenuId ) , 1 , entry ) ;
905 delete entry;
906 }
907 }
908
909 if ( GetAutoWindowMenu() )
910 {
911 if ( MacGetWindowMenuHMenu() == NULL )
912 CreateStandardWindowMenu( 0 , (MenuHandle*) &s_macWindowMenuHandle ) ;
913
914 InsertMenu( (MenuHandle) MacGetWindowMenuHMenu() , 0 ) ;
915 }
916
917 ::DrawMenuBar() ;
918#endif
919
920 s_macInstalledMenuBar = this;
921}
922
923void wxMenuBar::EnableTop(size_t pos, bool enable)
924{
925 wxCHECK_RET( IsAttached(), wxT("doesn't work with unattached menubars") );
03647350 926
8ceae028 927 m_rootMenu->FindItemByPosition(pos+firstMenuPos)->Enable(enable);
524c47aa
SC
928
929 Refresh();
930}
931
e4a23857
VZ
932bool wxMenuBar::IsEnabledTop(size_t pos) const
933{
934 wxCHECK_MSG( IsAttached(), true,
935 wxT("doesn't work with unattached menubars") );
936
937 wxMenuItem* const item = m_rootMenu->FindItemByPosition(pos+firstMenuPos);
938 wxCHECK_MSG( item, false, wxT("invalid menu index") );
939
940 return item->IsEnabled();
941}
942
524c47aa
SC
943bool wxMenuBar::Enable(bool enable)
944{
945 wxCHECK_MSG( IsAttached(), false, wxT("doesn't work with unattached menubars") );
946
947 size_t i;
948 for (i = 0; i < GetMenuCount(); i++)
949 EnableTop(i, enable);
950
951 return true;
952}
953
954void wxMenuBar::SetMenuLabel(size_t pos, const wxString& label)
955{
956 wxCHECK_RET( pos < GetMenuCount(), wxT("invalid menu index") );
957
8a145966 958 GetMenu(pos)->SetTitle( label ) ;
524c47aa
SC
959}
960
961wxString wxMenuBar::GetMenuLabel(size_t pos) const
962{
963 wxCHECK_MSG( pos < GetMenuCount(), wxEmptyString,
964 wxT("invalid menu index in wxMenuBar::GetMenuLabel") );
965
46405e36 966 return GetMenu(pos)->GetTitle();
524c47aa
SC
967}
968
969// ---------------------------------------------------------------------------
970// wxMenuBar construction
971// ---------------------------------------------------------------------------
972
524c47aa
SC
973wxMenu *wxMenuBar::Replace(size_t pos, wxMenu *menu, const wxString& title)
974{
975 wxMenu *menuOld = wxMenuBarBase::Replace(pos, menu, title);
976 if ( !menuOld )
977 return NULL;
978
524c47aa
SC
979 wxMenuItem* item = m_rootMenu->FindItemByPosition(pos+firstMenuPos);
980 m_rootMenu->Remove(item);
981 m_rootMenu->Insert( pos+firstMenuPos, wxMenuItem::New( m_rootMenu, wxID_ANY, title, "", wxITEM_NORMAL, menu ) );
982
524c47aa
SC
983 return menuOld;
984}
985
986bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title)
987{
988 if ( !wxMenuBarBase::Insert(pos, menu, title) )
989 return false;
990
524c47aa 991 m_rootMenu->Insert( pos+firstMenuPos, wxMenuItem::New( m_rootMenu, wxID_ANY, title, "", wxITEM_NORMAL, menu ) );
9014508c 992 menu->SetTitle(title);
524c47aa 993
524c47aa
SC
994 return true;
995}
996
997wxMenu *wxMenuBar::Remove(size_t pos)
998{
999 wxMenu *menu = wxMenuBarBase::Remove(pos);
1000 if ( !menu )
1001 return NULL;
1002
1003 wxMenuItem* item = m_rootMenu->FindItemByPosition(pos+firstMenuPos);
1004 m_rootMenu->Remove(item);
1005
524c47aa
SC
1006 return menu;
1007}
1008
1009bool wxMenuBar::Append(wxMenu *menu, const wxString& title)
1010{
1011 WXHMENU submenu = menu ? menu->GetHMenu() : 0;
1012 wxCHECK_MSG( submenu, false, wxT("can't append invalid menu to menubar") );
1013
1014 if ( !wxMenuBarBase::Append(menu, title) )
1015 return false;
1016
524c47aa 1017 m_rootMenu->AppendSubMenu(menu, title);
46405e36 1018 menu->SetTitle(title);
524c47aa 1019
524c47aa
SC
1020 return true;
1021}
1022
524c47aa
SC
1023void wxMenuBar::Detach()
1024{
1025 wxMenuBarBase::Detach() ;
1026}
1027
1028void wxMenuBar::Attach(wxFrame *frame)
1029{
1030 wxMenuBarBase::Attach( frame ) ;
1031}
1032
46405e36 1033#endif // wxUSE_MENUS