]> git.saurik.com Git - wxWidgets.git/blob - src/osx/cocoa/button.mm
Implement support for button mnemonics in wxOSX/Cocoa.
[wxWidgets.git] / src / osx / cocoa / button.mm
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/osx/cocoa/button.mm
3 // Purpose: wxButton
4 // Author: Stefan Csomor
5 // Modified by:
6 // Created: 1998-01-01
7 // RCS-ID: $Id$
8 // Copyright: (c) Stefan Csomor
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #include "wx/wxprec.h"
13
14 #ifndef WX_PRECOMP
15 #include "wx/object.h"
16 #endif
17
18 #include "wx/button.h"
19 #include "wx/toplevel.h"
20
21 #include "wx/osx/private.h"
22
23 #if wxUSE_MARKUP
24 #include "wx/osx/cocoa/private/markuptoattr.h"
25 #endif // wxUSE_MARKUP
26
27
28 @implementation wxNSButton
29
30 + (void)initialize
31 {
32 static BOOL initialized = NO;
33 if (!initialized)
34 {
35 initialized = YES;
36 wxOSXCocoaClassAddWXMethods( self );
37 }
38 }
39
40 - (int) intValue
41 {
42 switch ( [self state] )
43 {
44 case NSOnState:
45 return 1;
46 case NSMixedState:
47 return 2;
48 default:
49 return 0;
50 }
51 }
52
53 - (void) setIntValue: (int) v
54 {
55 switch( v )
56 {
57 case 2:
58 [self setState:NSMixedState];
59 break;
60 case 1:
61 [self setState:NSOnState];
62 break;
63 default :
64 [self setState:NSOffState];
65 break;
66 }
67 }
68
69 - (void) setTrackingTag: (NSTrackingRectTag)tag
70 {
71 rectTag = tag;
72 }
73
74 - (NSTrackingRectTag) trackingTag
75 {
76 return rectTag;
77 }
78
79 @end
80
81 @interface NSView(PossibleSizeMethods)
82 - (NSControlSize)controlSize;
83 @end
84
85 namespace
86 {
87
88 class wxButtonCocoaImpl : public wxWidgetCocoaImpl, public wxButtonImpl
89 {
90 public:
91 wxButtonCocoaImpl(wxWindowMac *wxpeer, wxNSButton *v)
92 : wxWidgetCocoaImpl(wxpeer, v)
93 {
94 SetNeedsFrame(false);
95 }
96
97 virtual void SetBitmap(const wxBitmap& bitmap)
98 {
99 // switch bezel style for plain pushbuttons
100 if ( bitmap.IsOk() )
101 {
102 if ([GetNSButton() bezelStyle] == NSRoundedBezelStyle)
103 [GetNSButton() setBezelStyle:NSRegularSquareBezelStyle];
104 }
105 else
106 {
107 [GetNSButton() setBezelStyle:NSRoundedBezelStyle];
108 }
109
110 wxWidgetCocoaImpl::SetBitmap(bitmap);
111 }
112
113 #if wxUSE_MARKUP
114 virtual void SetLabelMarkup(const wxString& markup)
115 {
116 wxMarkupToAttrString toAttr(GetWXPeer(), markup);
117 NSMutableAttributedString *attrString = toAttr.GetNSAttributedString();
118
119 // Button text is always centered.
120 NSMutableParagraphStyle *
121 paragraphStyle = [[NSMutableParagraphStyle alloc] init];
122 [paragraphStyle setAlignment: NSCenterTextAlignment];
123 [attrString addAttribute:NSParagraphStyleAttributeName
124 value:paragraphStyle
125 range:NSMakeRange(0, [attrString length])];
126 [paragraphStyle release];
127
128 [GetNSButton() setAttributedTitle:attrString];
129 }
130 #endif // wxUSE_MARKUP
131
132 void SetPressedBitmap( const wxBitmap& bitmap )
133 {
134 NSButton* button = GetNSButton();
135 [button setAlternateImage: bitmap.GetNSImage()];
136 [button setButtonType:NSMomentaryChangeButton];
137 }
138
139 void GetLayoutInset(int &left , int &top , int &right, int &bottom) const
140 {
141 left = top = right = bottom = 0;
142 NSControlSize size = NSRegularControlSize;
143 if ( [m_osxView respondsToSelector:@selector(controlSize)] )
144 size = [m_osxView controlSize];
145 else if ([m_osxView respondsToSelector:@selector(cell)])
146 {
147 id cell = [(id)m_osxView cell];
148 if ([cell respondsToSelector:@selector(controlSize)])
149 size = [cell controlSize];
150 }
151
152 if ( [GetNSButton() bezelStyle] == NSRoundedBezelStyle )
153 {
154 switch( size )
155 {
156 case NSRegularControlSize:
157 left = right = 6;
158 top = 4;
159 bottom = 8;
160 break;
161 case NSSmallControlSize:
162 left = right = 5;
163 top = 4;
164 bottom = 7;
165 break;
166 case NSMiniControlSize:
167 left = right = 1;
168 top = 0;
169 bottom = 2;
170 break;
171 }
172 }
173 }
174
175 void SetAcceleratorFromLabel(const wxString& label)
176 {
177 const int accelPos = wxControl::FindAccelIndex(label);
178 if ( accelPos != wxNOT_FOUND )
179 {
180 wxString accelstring(label[accelPos + 1]); // Skip '&' itself
181 accelstring.MakeLower();
182 wxCFStringRef cfText(accelstring);
183 [GetNSButton() setKeyEquivalent:cfText.AsNSString()];
184 [GetNSButton() setKeyEquivalentModifierMask:NSCommandKeyMask];
185 }
186 else
187 {
188 [GetNSButton() setKeyEquivalent:@""];
189 }
190 }
191
192
193 private:
194 NSButton *GetNSButton() const
195 {
196 wxASSERT( [m_osxView isKindOfClass:[NSButton class]] );
197
198 return static_cast<NSButton *>(m_osxView);
199 }
200 };
201
202 } // anonymous namespace
203
204 // Set the keyboard accelerator key from the label (e.g. "Click &Me")
205 void wxButton::OSXSetAcceleratorFromLabel(const wxString& label)
206 {
207 // Skip setting the accelerator for the default buttons as this would
208 // overwrite the default "Enter" which should be preserved.
209 wxTopLevelWindow * const
210 tlw = wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow);
211 if ( tlw )
212 {
213 if ( tlw->GetDefaultItem() == this )
214 return;
215 }
216
217 wxButtonCocoaImpl *impl = static_cast<wxButtonCocoaImpl*>(GetPeer());
218 impl->SetAcceleratorFromLabel(label);
219 }
220
221 extern "C" void SetBezelStyleFromBorderFlags(NSButton *v, long style);
222
223 // set bezel style depending on the wxBORDER_XXX flags specified by the style
224 void SetBezelStyleFromBorderFlags(NSButton *v, long style)
225 {
226 if ( style & wxBORDER_NONE )
227 {
228 [v setBezelStyle:NSShadowlessSquareBezelStyle];
229 [v setBordered:NO];
230 }
231 else // we do have a border
232 {
233 // see trac #11128 for a thorough discussion
234 if ( (style & wxBORDER_MASK) == wxBORDER_RAISED )
235 [v setBezelStyle:NSRegularSquareBezelStyle];
236 else if ( (style & wxBORDER_MASK) == wxBORDER_SUNKEN )
237 [v setBezelStyle:NSSmallSquareBezelStyle];
238 else if ( (style & wxBORDER_MASK) == wxBORDER_SIMPLE )
239 [v setBezelStyle:NSShadowlessSquareBezelStyle];
240 else
241 [v setBezelStyle:NSRegularSquareBezelStyle];
242 }
243 }
244
245
246 wxWidgetImplType* wxWidgetImpl::CreateButton( wxWindowMac* wxpeer,
247 wxWindowMac* WXUNUSED(parent),
248 wxWindowID id,
249 const wxString& label,
250 const wxPoint& pos,
251 const wxSize& size,
252 long style,
253 long WXUNUSED(extraStyle))
254 {
255 NSRect r = wxOSXGetFrameForControl( wxpeer, pos , size ) ;
256 wxNSButton* v = [[wxNSButton alloc] initWithFrame:r];
257
258 // We can't display a custom label inside a button with help bezel style so
259 // we only use it if we are using the default label. wxButton itself checks
260 // if the label is just "Help" in which case it discards it and passes us
261 // an empty string.
262 if ( id == wxID_HELP && label.empty() )
263 {
264 [v setBezelStyle:NSHelpButtonBezelStyle];
265 }
266 else
267 {
268 if ( style & wxBORDER_NONE )
269 {
270 [v setBezelStyle:NSShadowlessSquareBezelStyle];
271 [v setBordered:NO];
272 }
273 else
274 {
275 // the following styles only exist for certain sizes, so avoid them for
276 // multi-line
277 if ( label.Find('\n' ) == wxNOT_FOUND && label.Find('\r' ) == wxNOT_FOUND)
278 {
279 if ( (style & wxBORDER_MASK) == wxBORDER_RAISED )
280 [v setBezelStyle:NSRoundedBezelStyle];
281 else if ( (style & wxBORDER_MASK) == wxBORDER_SUNKEN )
282 [v setBezelStyle:NSTexturedRoundedBezelStyle];
283 else if ( (style & wxBORDER_MASK) == wxBORDER_SIMPLE )
284 [v setBezelStyle:NSShadowlessSquareBezelStyle];
285 else
286 [v setBezelStyle:NSRoundedBezelStyle];
287 }
288 else
289 {
290 if ( (style & wxBORDER_MASK) == wxBORDER_RAISED )
291 [v setBezelStyle:NSRegularSquareBezelStyle];
292 else if ( (style & wxBORDER_MASK) == wxBORDER_SUNKEN )
293 [v setBezelStyle:NSSmallSquareBezelStyle];
294 else if ( (style & wxBORDER_MASK) == wxBORDER_SIMPLE )
295 [v setBezelStyle:NSShadowlessSquareBezelStyle];
296 else
297 [v setBezelStyle:NSRegularSquareBezelStyle];
298 }
299
300 }
301 }
302
303 [v setButtonType:NSMomentaryPushInButton];
304 wxButtonCocoaImpl* const impl = new wxButtonCocoaImpl( wxpeer, v );
305 impl->SetAcceleratorFromLabel(label);
306 return impl;
307 }
308
309 void wxWidgetCocoaImpl::SetDefaultButton( bool isDefault )
310 {
311 if ( [m_osxView isKindOfClass:[NSButton class]] )
312 {
313 if ( isDefault )
314 {
315 [(NSButton*)m_osxView setKeyEquivalent: @"\r" ];
316 [(NSButton*)m_osxView setKeyEquivalentModifierMask: 0];
317 }
318 else
319 [(NSButton*)m_osxView setKeyEquivalent: @"" ];
320 }
321 }
322
323 void wxWidgetCocoaImpl::PerformClick()
324 {
325 if ([m_osxView isKindOfClass:[NSControl class]])
326 [(NSControl*)m_osxView performClick:nil];
327 }
328
329 #if wxUSE_BMPBUTTON
330
331 wxWidgetImplType* wxWidgetImpl::CreateBitmapButton( wxWindowMac* wxpeer,
332 wxWindowMac* WXUNUSED(parent),
333 wxWindowID WXUNUSED(id),
334 const wxBitmap& bitmap,
335 const wxPoint& pos,
336 const wxSize& size,
337 long style,
338 long WXUNUSED(extraStyle))
339 {
340 NSRect r = wxOSXGetFrameForControl( wxpeer, pos , size ) ;
341 wxNSButton* v = [[wxNSButton alloc] initWithFrame:r];
342
343 SetBezelStyleFromBorderFlags(v, style);
344
345 if (bitmap.IsOk())
346 [v setImage:bitmap.GetNSImage() ];
347
348 [v setButtonType:NSMomentaryPushInButton];
349 wxWidgetCocoaImpl* c = new wxButtonCocoaImpl( wxpeer, v );
350 return c;
351 }
352
353 #endif // wxUSE_BMPBUTTON
354
355 //
356 // wxDisclosureButton implementation
357 //
358
359 @interface wxDisclosureNSButton : NSButton
360 {
361
362 BOOL isOpen;
363 }
364
365 - (void) updateImage;
366
367 - (void) toggle;
368
369 + (NSImage *)rotateImage: (NSImage *)image;
370
371 @end
372
373 static const char * disc_triangle_xpm[] = {
374 "10 9 4 1",
375 " c None",
376 ". c #737373",
377 "+ c #989898",
378 "- c #c6c6c6",
379 " .- ",
380 " ..+- ",
381 " ....+ ",
382 " ......- ",
383 " .......- ",
384 " ......- ",
385 " ....+ ",
386 " ..+- ",
387 " .- ",
388 };
389
390 @implementation wxDisclosureNSButton
391
392 + (void)initialize
393 {
394 static BOOL initialized = NO;
395 if (!initialized)
396 {
397 initialized = YES;
398 wxOSXCocoaClassAddWXMethods( self );
399 }
400 }
401
402 - (id) initWithFrame:(NSRect) frame
403 {
404 self = [super initWithFrame:frame];
405 isOpen = NO;
406 [self setImagePosition:NSImageLeft];
407 [self updateImage];
408 return self;
409 }
410
411 - (int) intValue
412 {
413 return isOpen ? 1 : 0;
414 }
415
416 - (void) setIntValue: (int) v
417 {
418 isOpen = ( v != 0 );
419 [self updateImage];
420 }
421
422 - (void) toggle
423 {
424 isOpen = !isOpen;
425 [self updateImage];
426 }
427
428 wxCFRef<NSImage*> downArray ;
429
430 - (void) updateImage
431 {
432 static wxBitmap trianglebm(disc_triangle_xpm);
433 if ( downArray.get() == NULL )
434 {
435 downArray.reset( [[wxDisclosureNSButton rotateImage:trianglebm.GetNSImage()] retain] );
436 }
437
438 if ( isOpen )
439 [self setImage:(NSImage*)downArray.get()];
440 else
441 [self setImage:trianglebm.GetNSImage()];
442 }
443
444 + (NSImage *)rotateImage: (NSImage *)image
445 {
446 NSSize imageSize = [image size];
447 NSSize newImageSize = NSMakeSize(imageSize.height, imageSize.width);
448 NSImage* newImage = [[NSImage alloc] initWithSize: newImageSize];
449
450 [newImage lockFocus];
451
452 NSAffineTransform* tm = [NSAffineTransform transform];
453 [tm translateXBy:newImageSize.width/2 yBy:newImageSize.height/2];
454 [tm rotateByDegrees:-90];
455 [tm translateXBy:-newImageSize.width/2 yBy:-newImageSize.height/2];
456 [tm concat];
457
458
459 [image drawInRect:NSMakeRect(0,0,newImageSize.width, newImageSize.height)
460 fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0];
461
462 [newImage unlockFocus];
463 return [newImage autorelease];
464 }
465
466 @end
467
468 class wxDisclosureTriangleCocoaImpl : public wxWidgetCocoaImpl
469 {
470 public :
471 wxDisclosureTriangleCocoaImpl(wxWindowMac* peer , WXWidget w) :
472 wxWidgetCocoaImpl(peer, w)
473 {
474 }
475
476 ~wxDisclosureTriangleCocoaImpl()
477 {
478 }
479
480 virtual void controlAction(WXWidget slf, void* _cmd, void *sender)
481 {
482 wxDisclosureNSButton* db = (wxDisclosureNSButton*)m_osxView;
483 [db toggle];
484 wxWidgetCocoaImpl::controlAction(slf, _cmd, sender );
485 }
486 };
487
488 wxWidgetImplType* wxWidgetImpl::CreateDisclosureTriangle( wxWindowMac* wxpeer,
489 wxWindowMac* WXUNUSED(parent),
490 wxWindowID WXUNUSED(winid),
491 const wxString& label,
492 const wxPoint& pos,
493 const wxSize& size,
494 long style,
495 long WXUNUSED(extraStyle))
496 {
497 NSRect r = wxOSXGetFrameForControl( wxpeer, pos , size ) ;
498 wxDisclosureNSButton* v = [[wxDisclosureNSButton alloc] initWithFrame:r];
499 if ( !label.empty() )
500 [v setTitle:wxCFStringRef(label).AsNSString()];
501
502 SetBezelStyleFromBorderFlags(v, style);
503
504 return new wxDisclosureTriangleCocoaImpl( wxpeer, v );
505 }