Fix crash when auto-sizing a wxDataViewCtrl column.
[wxWidgets.git] / src / osx / cocoa / menuitem.mm
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name:        src/osx/cocoa/menuitem.mm
3 // Purpose:     wxMenuItem implementation
4 // Author:      Stefan Csomor
5 // Modified by:
6 // Created:     1998-01-01
7 // Copyright:   (c) Stefan Csomor
8 // Licence:     wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
10
11 #include "wx/wxprec.h"
12
13 #include "wx/menuitem.h"
14 #include "wx/stockitem.h"
15
16 #ifndef WX_PRECOMP
17     #include "wx/app.h"
18     #include "wx/log.h"
19     #include "wx/menu.h"
20 #endif // WX_PRECOMP
21
22 #include "wx/osx/private.h"
23
24 // a mapping from wx ids to standard osx actions in order to support the native menu item handling
25 // if a new mapping is added, make sure the wxNonOwnedWindowController has a handler for this action as well
26
27 struct Mapping
28 {
29     int menuid;
30     SEL action;
31 };
32
33 Mapping sActionToWXMapping[] =
34 {
35 // as we don't have NSUndoManager support we must not use the native actions
36 #if 0
37     { wxID_UNDO, @selector(undo:) },
38     { wxID_REDO, @selector(redo:) },
39 #endif
40     { wxID_CUT, @selector(cut:) },
41     { wxID_COPY, @selector(copy:) },
42     { wxID_PASTE, @selector(paste:) },
43     { wxID_CLEAR, @selector(delete:) },
44     { wxID_SELECTALL, @selector(selectAll:) },
45     { 0, NULL }
46 };
47
48 int wxOSXGetIdFromSelector(SEL action )
49 {
50     int i = 0 ;
51     while ( sActionToWXMapping[i].action != nil )
52     {
53         if ( sActionToWXMapping[i].action == action )
54             return sActionToWXMapping[i].menuid;
55         ++i;
56     }
57     
58     return 0;
59 }
60
61 SEL wxOSXGetSelectorFromID(int menuId )
62 {
63     int i = 0 ;
64     while ( sActionToWXMapping[i].action != nil )
65     {
66         if ( sActionToWXMapping[i].menuid == menuId )
67             return sActionToWXMapping[i].action;
68         ++i;
69     }
70     
71     return nil;
72 }
73
74
75 @implementation wxNSMenuItem
76
77 - (id) initWithTitle:(NSString *)aString action:(SEL)aSelector keyEquivalent:(NSString *)charCode
78 {
79     self = [super initWithTitle:aString action:aSelector keyEquivalent:charCode];
80     return self;
81 }
82
83 - (void) clickedAction: (id) sender
84 {
85     wxUnusedVar(sender);
86     if ( impl )
87     {
88         wxMenuItem* menuitem = impl->GetWXPeer();
89         if ( menuitem->GetMenu()->HandleCommandProcess(menuitem) == false )
90         {
91         }
92      }
93 }
94
95 - (void) setEnabled:(BOOL) flag
96 {
97     [super setEnabled:flag];
98 }
99
100 - (BOOL)validateMenuItem:(NSMenuItem *) menuItem
101 {
102     wxUnusedVar(menuItem);
103     if( impl )
104     {
105         wxMenuItem* wxmenuitem = impl->GetWXPeer();
106         if ( wxmenuitem )
107         {
108             wxmenuitem->GetMenu()->HandleCommandUpdateStatus(wxmenuitem);
109             return wxmenuitem->IsEnabled();
110         }
111     }
112     return YES ;
113 }
114
115 - (void)setImplementation: (wxMenuItemImpl *) theImplementation
116 {
117     impl = theImplementation;
118 }
119
120 - (wxMenuItemImpl*) implementation
121 {
122     return impl;
123 }
124
125 @end
126
127 void wxMacCocoaMenuItemSetAccelerator( NSMenuItem* menuItem, wxAcceleratorEntry* entry )
128 {
129     if ( entry == NULL )
130     {
131         [menuItem setKeyEquivalent:@""];
132         return;
133     }
134          
135     unsigned int modifiers = 0 ;
136     int key = entry->GetKeyCode() ;
137     if ( key )
138     {
139         if (entry->GetFlags() & wxACCEL_CTRL)
140             modifiers |= NSCommandKeyMask;
141
142         if (entry->GetFlags() & wxACCEL_RAW_CTRL)
143             modifiers |= NSControlKeyMask;
144         
145         if (entry->GetFlags() & wxACCEL_ALT)
146             modifiers |= NSAlternateKeyMask ;
147
148         // this may be ignored later for alpha chars
149
150         if (entry->GetFlags() & wxACCEL_SHIFT)
151             modifiers |= NSShiftKeyMask ;
152
153         unichar shortcut = 0;
154         if ( key >= WXK_F1 && key <= WXK_F15 )
155         {
156             modifiers |= NSFunctionKeyMask ;
157             shortcut = NSF1FunctionKey + ( key - WXK_F1 );
158         }
159         else
160         {
161             switch ( key )
162             {
163                 case WXK_CLEAR :
164                     modifiers |= NSFunctionKeyMask;
165                     shortcut = NSDeleteCharacter ;
166                     break ;
167
168                 case WXK_PAGEUP :
169                     modifiers |= NSFunctionKeyMask;
170                     shortcut = NSPageUpFunctionKey ;
171                     break ;
172
173                 case WXK_PAGEDOWN :
174                     modifiers |= NSFunctionKeyMask;
175                     shortcut = NSPageDownFunctionKey ;
176                     break ;
177
178                 case WXK_LEFT :
179                     modifiers |= NSNumericPadKeyMask | NSFunctionKeyMask;
180                     shortcut = NSLeftArrowFunctionKey ;
181                     break ;
182
183                 case WXK_UP :
184                     modifiers |= NSNumericPadKeyMask | NSFunctionKeyMask;
185                     shortcut = NSUpArrowFunctionKey ;
186                     break ;
187
188                 case WXK_RIGHT :
189                     modifiers |= NSNumericPadKeyMask | NSFunctionKeyMask;
190                     shortcut = NSRightArrowFunctionKey ;
191                     break ;
192
193                 case WXK_DOWN :
194                     modifiers |= NSNumericPadKeyMask | NSFunctionKeyMask;
195                     shortcut = NSDownArrowFunctionKey ;
196                     break ;
197
198                 case WXK_HOME :
199                     modifiers |= NSFunctionKeyMask;
200                     shortcut = NSHomeFunctionKey ;
201                     break ;
202
203                 case WXK_END :
204                     modifiers |= NSFunctionKeyMask;
205                     shortcut = NSEndFunctionKey ;
206                     break ;
207
208                 case WXK_NUMPAD_ENTER :
209                     shortcut = NSEnterCharacter;
210                     break;
211                     
212                 case WXK_BACK :
213                 case WXK_RETURN :
214                 case WXK_TAB :
215                 case WXK_ESCAPE :
216                 default :
217                     if(entry->GetFlags() & wxACCEL_SHIFT)
218                         shortcut = toupper(key);
219                     else
220                         shortcut = tolower(key);
221                     break ;
222             }
223         }
224
225         [menuItem setKeyEquivalent:[NSString stringWithCharacters:&shortcut length:1]];
226         [menuItem setKeyEquivalentModifierMask:modifiers];
227     }
228 }
229
230 @interface NSMenuItem(PossibleMethods)
231 - (void)setHidden:(BOOL)hidden;
232 @end
233
234 class wxMenuItemCocoaImpl : public wxMenuItemImpl
235 {
236 public :
237     wxMenuItemCocoaImpl( wxMenuItem* peer, NSMenuItem* item ) : wxMenuItemImpl(peer), m_osxMenuItem(item)
238     {
239         if ( ![m_osxMenuItem isSeparatorItem] )
240             [(wxNSMenuItem*)m_osxMenuItem setImplementation:this];
241     }
242
243     ~wxMenuItemCocoaImpl();
244
245     void SetBitmap( const wxBitmap& bitmap )
246     {
247         [m_osxMenuItem setImage:bitmap.GetNSImage()];
248     }
249
250     void Enable( bool enable )
251     {
252         [m_osxMenuItem setEnabled:enable];
253     }
254
255     void Check( bool check )
256     {
257         [m_osxMenuItem setState:( check ?  NSOnState :  NSOffState) ];
258     }
259
260     void Hide( bool hide )
261     {
262         // NB: setHidden is new as of 10.5 so we should not call it below there
263         if ([m_osxMenuItem respondsToSelector:@selector(setHidden:)])
264             [m_osxMenuItem setHidden:hide ];
265         else
266             wxLogDebug("wxMenuItemCocoaImpl::Hide not yet supported under OS X < 10.5");
267     }
268
269     void SetLabel( const wxString& text, wxAcceleratorEntry *entry )
270     {
271         wxCFStringRef cfText(text);
272         [m_osxMenuItem setTitle:cfText.AsNSString()];
273
274         wxMacCocoaMenuItemSetAccelerator( m_osxMenuItem, entry );
275     }
276     
277     bool DoDefault();
278
279     void * GetHMenuItem() { return m_osxMenuItem; }
280
281 protected :
282     NSMenuItem* m_osxMenuItem ;
283 } ;
284
285 wxMenuItemCocoaImpl::~wxMenuItemCocoaImpl()
286 {
287     if ( ![m_osxMenuItem isSeparatorItem] )
288         [(wxNSMenuItem*)m_osxMenuItem setImplementation:nil];
289     [m_osxMenuItem release];
290 }
291
292 bool wxMenuItemCocoaImpl::DoDefault()
293 {
294     bool handled=false;
295     int menuid = m_peer->GetId();
296     
297     NSApplication *theNSApplication = [NSApplication sharedApplication];
298     if (menuid == wxID_OSX_HIDE)
299     {
300         [theNSApplication hide:nil];
301         handled=true;
302     }
303     else if (menuid == wxID_OSX_HIDEOTHERS)
304     {
305         [theNSApplication hideOtherApplications:nil];
306         handled=true;
307     }
308     else if (menuid == wxID_OSX_SHOWALL)
309     {
310         [theNSApplication unhideAllApplications:nil];
311         handled=true;
312     }
313     else if (menuid == wxApp::s_macExitMenuItemId)
314     {
315         wxTheApp->ExitMainLoop();
316     }
317     return handled;
318 }
319
320 wxMenuItemImpl* wxMenuItemImpl::Create( wxMenuItem* peer, wxMenu *pParentMenu,
321                        int menuid,
322                        const wxString& text,
323                        wxAcceleratorEntry *entry,
324                        const wxString& WXUNUSED(strHelp),
325                        wxItemKind kind,
326                        wxMenu *pSubMenu )
327 {
328     wxMenuItemImpl* c = NULL;
329     NSMenuItem* item = nil;
330
331     if ( kind == wxITEM_SEPARATOR )
332     {
333         item = [[NSMenuItem separatorItem] retain];
334     }
335     else
336     {
337         wxCFStringRef cfText(text);
338         SEL selector = nil;
339         bool targetSelf = false;
340         if ( (pParentMenu == NULL || !pParentMenu->GetNoEventsMode()) && pSubMenu == NULL )
341         {
342             selector = wxOSXGetSelectorFromID(menuid);
343             
344             if ( selector == nil )
345             {
346                 selector = @selector(clickedAction:);
347                 targetSelf = true;
348             }
349         }
350         
351         wxNSMenuItem* menuitem = [ [ wxNSMenuItem alloc ] initWithTitle:cfText.AsNSString() action:selector keyEquivalent:@""];
352         if ( targetSelf )
353             [menuitem setTarget:menuitem];
354         
355         if ( pSubMenu )
356         {
357             pSubMenu->GetPeer()->SetTitle( text );
358             [menuitem setSubmenu:pSubMenu->GetHMenu()];
359         }
360         else
361         {
362             wxMacCocoaMenuItemSetAccelerator( menuitem, entry );
363         }
364         item = menuitem;
365     }
366     c = new wxMenuItemCocoaImpl( peer, item );
367     return c;
368 }