]> git.saurik.com Git - wxWidgets.git/blame - src/osx/cocoa/button.mm
Add wxTEST_DIALOG for testing of modal dialogs.
[wxWidgets.git] / src / osx / cocoa / button.mm
CommitLineData
f033830e
SC
1/////////////////////////////////////////////////////////////////////////////
2// Name: src/osx/cocoa/button.mm
3// Purpose: wxButton
4// Author: Stefan Csomor
5// Modified by:
6// Created: 1998-01-01
a9a4f229 7// RCS-ID: $Id$
f033830e
SC
8// Copyright: (c) Stefan Csomor
9// Licence: wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12#include "wx/wxprec.h"
13
f033830e 14#ifndef WX_PRECOMP
d6eb3ff8 15#include "wx/object.h"
f033830e
SC
16#endif
17
d6eb3ff8 18#include "wx/button.h"
0afa3752 19#include "wx/toplevel.h"
d6eb3ff8 20
f033830e
SC
21#include "wx/osx/private.h"
22
f672c969
VZ
23#if wxUSE_MARKUP
24 #include "wx/osx/cocoa/private/markuptoattr.h"
25#endif // wxUSE_MARKUP
26
27
f033830e
SC
28@implementation wxNSButton
29
4dd9fdf8 30+ (void)initialize
f033830e 31{
4dd9fdf8 32 static BOOL initialized = NO;
b727fcd3 33 if (!initialized)
f033830e 34 {
4dd9fdf8
SC
35 initialized = YES;
36 wxOSXCocoaClassAddWXMethods( self );
f033830e
SC
37 }
38}
39
f033830e
SC
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
411a1c35
SC
69- (void) setTrackingTag: (NSTrackingRectTag)tag
70{
71 rectTag = tag;
72}
73
74- (NSTrackingRectTag) trackingTag
75{
76 return rectTag;
77}
78
f033830e
SC
79@end
80
58ce18f2
SC
81@interface NSView(PossibleSizeMethods)
82- (NSControlSize)controlSize;
83@end
84
e5d05b90
VZ
85namespace
86{
87
b38dc31f 88class wxButtonCocoaImpl : public wxWidgetCocoaImpl, public wxButtonImpl
e5d05b90
VZ
89{
90public:
91 wxButtonCocoaImpl(wxWindowMac *wxpeer, wxNSButton *v)
92 : wxWidgetCocoaImpl(wxpeer, v)
93 {
24e059c3 94 SetNeedsFrame(false);
e5d05b90
VZ
95 }
96
97 virtual void SetBitmap(const wxBitmap& bitmap)
98 {
b38dc31f 99 // switch bezel style for plain pushbuttons
b8702c67
SC
100 if ( bitmap.IsOk() )
101 {
102 if ([GetNSButton() bezelStyle] == NSRoundedBezelStyle)
103 [GetNSButton() setBezelStyle:NSRegularSquareBezelStyle];
104 }
105 else
106 {
107 [GetNSButton() setBezelStyle:NSRoundedBezelStyle];
108 }
109
e5d05b90
VZ
110 wxWidgetCocoaImpl::SetBitmap(bitmap);
111 }
112
6a219e34 113#if wxUSE_MARKUP
f672c969
VZ
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 }
6a219e34 130#endif // wxUSE_MARKUP
f672c969 131
b38dc31f
SC
132 void SetPressedBitmap( const wxBitmap& bitmap )
133 {
134 NSButton* button = GetNSButton();
135 [button setAlternateImage: bitmap.GetNSImage()];
136 [button setButtonType:NSMomentaryChangeButton];
137 }
138
54ea2834
SC
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)] )
58ce18f2 144 size = [m_osxView controlSize];
54ea2834
SC
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 }
0afa3752
VZ
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 }
54ea2834
SC
191
192
e5d05b90
VZ
193private:
194 NSButton *GetNSButton() const
195 {
196 wxASSERT( [m_osxView isKindOfClass:[NSButton class]] );
197
198 return static_cast<NSButton *>(m_osxView);
199 }
200};
201
df04f800
SC
202} // anonymous namespace
203
0afa3752
VZ
204// Set the keyboard accelerator key from the label (e.g. "Click &Me")
205void 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
7f08aa6c
SC
221extern "C" void SetBezelStyleFromBorderFlags(NSButton *v, long style);
222
73b1b996
VZ
223// set bezel style depending on the wxBORDER_XXX flags specified by the style
224void 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];
24e059c3 238 else if ( (style & wxBORDER_MASK) == wxBORDER_SIMPLE )
73b1b996 239 [v setBezelStyle:NSShadowlessSquareBezelStyle];
24e059c3
SC
240 else
241 [v setBezelStyle:NSRegularSquareBezelStyle];
73b1b996
VZ
242 }
243}
244
f033830e 245
b727fcd3
VZ
246wxWidgetImplType* wxWidgetImpl::CreateButton( wxWindowMac* wxpeer,
247 wxWindowMac* WXUNUSED(parent),
248 wxWindowID id,
01495abf 249 const wxString& label,
b727fcd3 250 const wxPoint& pos,
f033830e 251 const wxSize& size,
24e059c3 252 long style,
b727fcd3 253 long WXUNUSED(extraStyle))
f033830e 254{
dbeddfb9 255 NSRect r = wxOSXGetFrameForControl( wxpeer, pos , size ) ;
f033830e 256 wxNSButton* v = [[wxNSButton alloc] initWithFrame:r];
b727fcd3 257
01495abf
VZ
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() )
f033830e
SC
263 {
264 [v setBezelStyle:NSHelpButtonBezelStyle];
265 }
266 else
267 {
24e059c3
SC
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 }
b727fcd3 299
24e059c3
SC
300 }
301 }
0afa3752 302
f033830e 303 [v setButtonType:NSMomentaryPushInButton];
0afa3752
VZ
304 wxButtonCocoaImpl* const impl = new wxButtonCocoaImpl( wxpeer, v );
305 impl->SetAcceleratorFromLabel(label);
306 return impl;
f033830e
SC
307}
308
309void wxWidgetCocoaImpl::SetDefaultButton( bool isDefault )
b727fcd3 310{
3f30bd1a
SC
311 if ( [m_osxView isKindOfClass:[NSButton class]] )
312 {
313 if ( isDefault )
0afa3752 314 {
3f30bd1a 315 [(NSButton*)m_osxView setKeyEquivalent: @"\r" ];
0afa3752
VZ
316 [(NSButton*)m_osxView setKeyEquivalentModifierMask: 0];
317 }
3f30bd1a
SC
318 else
319 [(NSButton*)m_osxView setKeyEquivalent: @"" ];
320 }
f033830e
SC
321}
322
b727fcd3 323void wxWidgetCocoaImpl::PerformClick()
f033830e 324{
73b1b996
VZ
325 if ([m_osxView isKindOfClass:[NSControl class]])
326 [(NSControl*)m_osxView performClick:nil];
f033830e
SC
327}
328
b38dc31f
SC
329#if wxUSE_BMPBUTTON
330
331wxWidgetImplType* 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];
73b1b996
VZ
342
343 SetBezelStyleFromBorderFlags(v, style);
344
a1b806b9 345 if (bitmap.IsOk())
b38dc31f 346 [v setImage:bitmap.GetNSImage() ];
73b1b996 347
b38dc31f
SC
348 [v setButtonType:NSMomentaryPushInButton];
349 wxWidgetCocoaImpl* c = new wxButtonCocoaImpl( wxpeer, v );
350 return c;
351}
352
73b1b996 353#endif // wxUSE_BMPBUTTON
b38dc31f 354
17ad51ed
SC
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
da52d42b
SC
373static 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
17ad51ed
SC
390@implementation wxDisclosureNSButton
391
392+ (void)initialize
393{
394 static BOOL initialized = NO;
b727fcd3 395 if (!initialized)
17ad51ed
SC
396 {
397 initialized = YES;
398 wxOSXCocoaClassAddWXMethods( self );
399 }
400}
401
402- (id) initWithFrame:(NSRect) frame
403{
404 self = [super initWithFrame:frame];
17ad51ed
SC
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
428wxCFRef<NSImage*> downArray ;
429
430- (void) updateImage
431{
da52d42b 432 static wxBitmap trianglebm(disc_triangle_xpm);
17ad51ed
SC
433 if ( downArray.get() == NULL )
434 {
adf264f2 435 downArray.reset( [[wxDisclosureNSButton rotateImage:trianglebm.GetNSImage()] retain] );
17ad51ed 436 }
b727fcd3 437
17ad51ed
SC
438 if ( isOpen )
439 [self setImage:(NSImage*)downArray.get()];
440 else
da52d42b 441 [self setImage:trianglebm.GetNSImage()];
17ad51ed
SC
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];
b727fcd3 449
17ad51ed 450 [newImage lockFocus];
b727fcd3 451
17ad51ed
SC
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];
b727fcd3
VZ
457
458
17ad51ed
SC
459 [image drawInRect:NSMakeRect(0,0,newImageSize.width, newImageSize.height)
460 fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0];
b727fcd3 461
17ad51ed 462 [newImage unlockFocus];
adf264f2 463 return [newImage autorelease];
17ad51ed
SC
464}
465
466@end
467
468class wxDisclosureTriangleCocoaImpl : public wxWidgetCocoaImpl
469{
470public :
471 wxDisclosureTriangleCocoaImpl(wxWindowMac* peer , WXWidget w) :
472 wxWidgetCocoaImpl(peer, w)
473 {
474 }
b727fcd3 475
17ad51ed
SC
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
b727fcd3
VZ
488wxWidgetImplType* wxWidgetImpl::CreateDisclosureTriangle( wxWindowMac* wxpeer,
489 wxWindowMac* WXUNUSED(parent),
73b1b996 490 wxWindowID WXUNUSED(winid),
dbeddfb9 491 const wxString& label,
b727fcd3 492 const wxPoint& pos,
dbeddfb9 493 const wxSize& size,
73b1b996 494 long style,
b727fcd3 495 long WXUNUSED(extraStyle))
dbeddfb9 496{
dbeddfb9 497 NSRect r = wxOSXGetFrameForControl( wxpeer, pos , size ) ;
17ad51ed 498 wxDisclosureNSButton* v = [[wxDisclosureNSButton alloc] initWithFrame:r];
73b1b996
VZ
499 if ( !label.empty() )
500 [v setTitle:wxCFStringRef(label).AsNSString()];
501
502 SetBezelStyleFromBorderFlags(v, style);
503
504 return new wxDisclosureTriangleCocoaImpl( wxpeer, v );
dbeddfb9 505}