1/////////////////////////////////////////////////////////////////////////////
2// Name:        src/cocoa/menu.mm
3// Purpose:     wxMenu and wxMenuBar implementation
4// Author:      David Elliott
5// Modified by:
6// Created:     2002/12/09
7// Copyright:   (c) 2002 David Elliott
8// Licence:     wxWindows licence
9/////////////////////////////////////////////////////////////////////////////
10
11// ============================================================================
12// declarations
13// ============================================================================
14
15// ----------------------------------------------------------------------------
16// headers
17// ----------------------------------------------------------------------------
18
19#include "wx/wxprec.h"
20
21#include "wx/menu.h"
22
23#ifndef WX_PRECOMP
24    #include "wx/log.h"
25#endif // WX_PRECOMP
26
27#include "wx/cocoa/autorelease.h"
28#include "wx/cocoa/string.h"
29
30#import <Foundation/NSString.h>
31#include "wx/cocoa/objc/NSMenu.h"
32
33#if wxUSE_MENUS
34
35// ----------------------------------------------------------------------------
36// globals
37// ----------------------------------------------------------------------------
38
39// ============================================================================
40// wxMenu implementation
41// ============================================================================
42
43bool wxMenu::Create(const wxString& title, long style)
44{
45    wxAutoNSAutoreleasePool pool;
46    m_cocoaNSMenu = [[WX_GET_OBJC_CLASS(WXNSMenu) alloc] initWithTitle: wxNSStringWithWxString(title)];
47    AssociateNSMenu(m_cocoaNSMenu);
48    return true;
49}
50
51wxMenu::~wxMenu()
52{
53    DisassociateNSMenu(m_cocoaNSMenu);
54    if(!m_cocoaDeletes)
55        [m_cocoaNSMenu release];
56}
57
58wxMenuItem* wxMenu::DoAppend(wxMenuItem *item)
59{
60    wxAutoNSAutoreleasePool pool;
61    if(!wxMenuBase::DoAppend(item))
62        return NULL;
63    [m_cocoaNSMenu addItem: item->GetNSMenuItem()];
64    return item;
65}
66
67wxMenuItem* wxMenu::DoInsert(unsigned long pos, wxMenuItem *item)
68{
69    wxAutoNSAutoreleasePool pool;
70    if(!wxMenuBase::DoInsert(pos,item))
71        return NULL;
72    [m_cocoaNSMenu insertItem:item->GetNSMenuItem() atIndex:pos];
73    return item;
74}
75
76wxMenuItem* wxMenu::DoRemove(wxMenuItem *item)
77{
78    wxAutoNSAutoreleasePool pool;
79    wxMenuItem *retitem = wxMenuBase::DoRemove(item);
80    wxASSERT(retitem->GetNSMenuItem());
81    [m_cocoaNSMenu removeItem:retitem->GetNSMenuItem()];
82    return retitem;
83}
84
85// This autoreleases the menu on the assumption that something is
86// going to retain it shortly (for instance, it is going to be returned from
87// an overloaded [NSStatusItem menu] or from the applicationDockMenu:
88// NSApplication delegate method.
89//
90// It then sets a bool flag m_cocoaDeletes.  When the NSMenu is dealloc'd
91// (dealloc is the Cocoa destructor) we delete ourselves.  In this manner we
92// can be available for Cocoa calls until Cocoa is finished with us.
93//
94// I can see very few reasons to undo this.  Nevertheless, it is implemented.
95void wxMenu::SetCocoaDeletes(bool cocoaDeletes)
96{
97    if(m_cocoaDeletes==cocoaDeletes)
98        return;
99    m_cocoaDeletes = cocoaDeletes;
100    if(m_cocoaDeletes)
101        [m_cocoaNSMenu autorelease];
102    else
103        [m_cocoaNSMenu retain];
104}
105
106void wxMenu::Cocoa_dealloc()
107{
108    if(m_cocoaDeletes)
109        delete this;
110}
111
112// ============================================================================
113// wxMenuBar implementation
114// ============================================================================
115
116bool wxMenuBar::Create(long style)
117{
118    wxAutoNSAutoreleasePool pool;
119    m_cocoaNSMenu = [[NSMenu alloc] initWithTitle: @"wxMenuBar"];
120
121    NSMenuItem *dummyItem = [[NSMenuItem alloc] initWithTitle:@"App menu"
122        /* Note: title gets clobbered by app name anyway */
123        action:nil keyEquivalent:@""];
124    [m_cocoaNSMenu addItem:dummyItem];
125    [dummyItem release];
126    return true;
127}
128
129wxMenuBar::wxMenuBar(size_t n,
130                     wxMenu *menus[],
131                     const wxString titles[],
132                     long style)
133{
134    Create(style);
135
136    for ( size_t i = 0; i < n; ++i )
137        Append(menus[i], titles[i]);
138}
139
140wxMenuBar::~wxMenuBar()
141{
142    [m_cocoaNSMenu release];
143}
144
145bool wxMenuBar::Append( wxMenu *menu, const wxString &title )
146{
147    wxAutoNSAutoreleasePool pool;
148    wxLogTrace(wxTRACE_COCOA,wxT("append menu=%p, title=%s"),menu,title.c_str());
149    if(!wxMenuBarBase::Append(menu,title))
150        return false;
151    wxASSERT(menu);
152    wxASSERT(menu->GetNSMenu());
153    NSString *menuTitle = wxInitNSStringWithWxString([NSString alloc], wxStripMenuCodes(title));
154    NSMenuItem *newItem = [[NSMenuItem alloc] initWithTitle:menuTitle action:NULL keyEquivalent:@""];
155    [menu->GetNSMenu() setTitle:menuTitle];
156    [newItem setSubmenu:menu->GetNSMenu()];
157
158    [m_cocoaNSMenu addItem:newItem];
159
160    [menuTitle release];
161    [newItem release];
162    return true;
163}
164
165bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title)
166{
167    wxAutoNSAutoreleasePool pool;
168    wxLogTrace(wxTRACE_COCOA,wxT("insert pos=%lu, menu=%p, title=%s"),pos,menu,title.c_str());
169    // Get the current menu at this position
170    wxMenu *nextmenu = GetMenu(pos);
171    if(!wxMenuBarBase::Insert(pos,menu,title))
172        return false;
173    wxASSERT(menu);
174    wxASSERT(menu->GetNSMenu());
175    NSString *menuTitle = wxInitNSStringWithWxString([NSString alloc], title);
176    NSMenuItem *newItem = [[NSMenuItem alloc] initWithTitle:menuTitle action:NULL keyEquivalent:@""];
177    [menu->GetNSMenu() setTitle:menuTitle];
178    [newItem setSubmenu:menu->GetNSMenu()];
179
180    int itemindex = [m_cocoaNSMenu indexOfItemWithSubmenu:nextmenu->GetNSMenu()];
181    wxASSERT(itemindex>=0);
182    [m_cocoaNSMenu insertItem:newItem atIndex:itemindex];
183
184    [menuTitle release];
185    [newItem release];
186    return true;
187}
188
189wxMenu *wxMenuBar::Replace(size_t pos, wxMenu *menu, const wxString& title)
190{
191    return NULL;
192}
193
194wxMenu *wxMenuBar::Remove(size_t pos)
195{
196    wxMenu *menu = wxMenuBarBase::Remove(pos);
197    wxASSERT(menu);
198    int itemindex = [GetNSMenu() indexOfItemWithSubmenu:menu->GetNSMenu()];
199    wxASSERT(itemindex>=0);
200    [m_cocoaNSMenu removeItemAtIndex:itemindex];
201    return menu;
202}
203
204
205void wxMenuBar::EnableTop(size_t pos, bool enable)
206{
207}
208
209bool wxMenuBar::IsEnabledTop(size_t pos) const
210{
211    return false;
212}
213
214void wxMenuBar::SetMenuLabel(size_t pos, const wxString& label)
215{
216}
217
218wxString wxMenuBar::GetMenuLabel(size_t pos) const
219{
220    wxMenu *menu = GetMenu(pos);
221    int itemindex = [m_cocoaNSMenu indexOfItemWithSubmenu:menu->GetNSMenu()];
222    wxASSERT(itemindex>=0);
223    return wxStringWithNSString([[m_cocoaNSMenu itemAtIndex:itemindex] title]);
224}
225
226void wxMenuBar::Attach(wxFrame *frame)
227{
228    wxMenuBarBase::Attach(frame);
229}
230
231void wxMenuBar::Detach()
232{
233    wxMenuBarBase::Detach();
234}
235
236wxSize wxMenuBar::DoGetBestClientSize() const
237{
238    return wxDefaultSize;
239}
240
241#endif // wxUSE_MENUS
242