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