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