1/** <title>NSButtonCell</title> 2 3 <abstract>The button cell class</abstract> 4 5 Copyright (C) 1996-1999 Free Software Foundation, Inc. 6 7 Author: Scott Christley <scottc@net-community.com> 8 Ovidiu Predescu <ovidiu@net-community.com> 9 Date: 1996 10 Author: Felipe A. Rodriguez <far@ix.netcom.com> 11 Date: August 1998 12 13 Modified: Richard Frith-Macdonald <richard@brainstorm.co.uk> 14 15 This file is part of the GNUstep GUI Library. 16 17 This library is free software; you can redistribute it and/or 18 modify it under the terms of the GNU Lesser General Public 19 License as published by the Free Software Foundation; either 20 version 2 of the License, or (at your option) any later version. 21 22 This library is distributed in the hope that it will be useful, 23 but WITHOUT ANY WARRANTY; without even the implied warranty of 24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 25 Lesser General Public License for more details. 26 27 You should have received a copy of the GNU Lesser General Public 28 License along with this library; see the file COPYING.LIB. 29 If not, see <http://www.gnu.org/licenses/> or write to the 30 Free Software Foundation, 51 Franklin Street, Fifth Floor, 31 Boston, MA 02110-1301, USA. 32*/ 33 34#import "config.h" 35#import <Foundation/NSArray.h> 36#import <Foundation/NSAttributedString.h> 37#import <Foundation/NSDebug.h> 38#import <Foundation/NSException.h> 39#import <Foundation/NSLock.h> 40#import <Foundation/NSString.h> 41#import <Foundation/NSValue.h> 42 43#import "AppKit/AppKitExceptions.h" 44#import "AppKit/NSApplication.h" 45#import "AppKit/NSBezierPath.h" 46#import "AppKit/NSButtonCell.h" 47#import "AppKit/NSButton.h" 48#import "AppKit/NSColor.h" 49#import "AppKit/NSEvent.h" 50#import "AppKit/NSFont.h" 51#import "AppKit/NSGraphics.h" 52#import "AppKit/NSImage.h" 53#import "AppKit/NSSound.h" 54#import "AppKit/NSStringDrawing.h" 55#import "AppKit/NSWindow.h" 56#import "GNUstepGUI/GSTheme.h" 57#import "GNUstepGUI/GSNibLoading.h" 58#import "GSGuiPrivate.h" 59#import "GSCodingFlags.h" 60 61#include <math.h> 62 63@interface NSCell (Private) 64- (NSSize) _scaleImageWithSize: (NSSize)imageSize 65 toFitInSize: (NSSize)canvasSize 66 scalingType: (NSImageScaling)scalingType; 67@end 68 69/**<p> TODO Description</p> 70 */ 71@implementation NSButtonCell 72 73/* 74 * Class methods 75 */ 76+ (void) initialize 77{ 78 if (self == [NSButtonCell class]) 79 [self setVersion: 3]; 80} 81 82/* 83 * Instance methods 84 */ 85- (id) _init 86{ 87 // Implicitly performed by allocation: 88 // 89 //_buttoncell_is_transparent = NO; 90 91 [self setAlignment: NSCenterTextAlignment]; 92 _cell.is_bordered = YES; 93 [self setButtonType: NSMomentaryPushInButton]; 94 _delayInterval = 0.4; 95 _repeatInterval = 0.075; 96 _keyEquivalentModifierMask = 0; 97 _keyEquivalent = @""; 98 _altContents = @""; 99 _gradient_type = NSGradientNone; 100 [self setImageScaling: NSImageScaleNone]; 101 102 return self; 103} 104 105- (id) init 106{ 107 self = [self initTextCell: @"Button"]; 108 109 return self; 110} 111 112- (id) initImageCell: (NSImage*)anImage 113{ 114 self = [super initImageCell: anImage]; 115 if (!self) 116 return nil; 117 118 return [self _init]; 119} 120 121- (id) initTextCell: (NSString*)aString 122{ 123 self = [super initTextCell: aString]; 124 if (!self) 125 return nil; 126 127 return [self _init]; 128} 129 130- (void) dealloc 131{ 132 RELEASE(_altContents); 133 RELEASE(_altImage); 134 RELEASE(_keyEquivalent); 135 RELEASE(_keyEquivalentFont); 136 RELEASE(_sound); 137 RELEASE(_backgroundColor); 138 139 [super dealloc]; 140} 141 142/** 143 *<p>The GNUstep implementation does nothing here 144 * (to match the Mac OS X behavior) because with 145 * NSButtonCell GNUstep implementation the cell type is bound to the image 146 * position. We implemented this behavior because it permits to have 147 * -setFont: -setTitle -setImage: methods which are symetrical by not altering 148 * directly the cell type and to validate the fact that the cell type is more 149 * characterized by the potential visibility of the image (which is under the 150 * control of the method -setImagePosition:) than by the value of the image 151 * ivar itself (related to -setImage: method). 152 * On Mac OS X, the NSButtonCell cell type is NSTextCellType by default or 153 * NSImageCellType if the initialization has been done with -initImageCell:, 154 * it should be noted that the cell type never changes later.</p> 155 */ 156- (void) setType: (NSCellType)aType 157{ 158} 159 160/** <p>Returns the NSButtonCell's title.</p> 161 */ 162- (NSString*) title 163{ 164 if (nil == _contents) 165 { 166 return @""; 167 } 168 169 if (_cell.contents_is_attributed_string == NO) 170 { 171 // If we have a formatter this is also the string of the _object_value 172 return (NSString *)_contents; 173 } 174 else 175 { 176 return [(NSAttributedString *)_contents string]; 177 } 178} 179 180/** <p>Returns the NSButtonCell's alternate title ( used when highlighted ). 181 </p><p>See Also: -setAlternateTitle:</p> 182 */ 183- (NSString*) alternateTitle 184{ 185 if (_altContents != nil) 186 { 187 return _altContents; 188 } 189 else 190 { 191 return @""; 192 } 193} 194 195- (NSInteger) cellAttribute: (NSCellAttribute)aParameter 196{ 197 NSInteger value = 0; 198 199 switch (aParameter) 200 { 201 case NSPushInCell: 202 if (_highlightsByMask & NSPushInCellMask) 203 value = 1; 204 break; 205 case NSChangeGrayCell: 206 if (_showAltStateMask & NSChangeGrayCellMask) 207 value = 1; 208 break; 209 case NSCellLightsByGray: 210 if (_highlightsByMask & NSChangeGrayCellMask) 211 value = 1; 212 break; 213 case NSChangeBackgroundCell: 214 if (_showAltStateMask & NSChangeBackgroundCellMask) 215 value = 1; 216 break; 217 case NSCellLightsByBackground: 218 if (_highlightsByMask & NSChangeBackgroundCellMask) 219 value = 1; 220 break; 221 case NSCellChangesContents: 222 if (_showAltStateMask & NSContentsCellMask) 223 value = 1; 224 break; 225 case NSCellLightsByContents: 226 if (_highlightsByMask & NSContentsCellMask) 227 value = 1; 228 break; 229 default: 230 value = [super cellAttribute: aParameter]; 231 break; 232 } 233 234 return value; 235} 236 237- (void) setCellAttribute: (NSCellAttribute)aParameter to: (NSInteger)value 238{ 239 switch (aParameter) 240 { 241 case NSPushInCell: 242 if (value) 243 _highlightsByMask |= NSPushInCellMask; 244 else 245 _highlightsByMask &= ~NSPushInCellMask; 246 break; 247 case NSChangeGrayCell: 248 if (value) 249 _showAltStateMask |= NSChangeGrayCellMask; 250 else 251 _showAltStateMask &= ~NSChangeGrayCellMask; 252 break; 253 case NSChangeBackgroundCell: 254 if (value) 255 _showAltStateMask |= NSChangeBackgroundCellMask; 256 else 257 _showAltStateMask &= ~NSChangeBackgroundCellMask; 258 break; 259 case NSCellChangesContents: 260 if (value) 261 _showAltStateMask |= NSContentsCellMask; 262 else 263 _showAltStateMask &= ~NSContentsCellMask; 264 break; 265 case NSCellLightsByGray: 266 if (value) 267 _highlightsByMask |= NSChangeGrayCellMask; 268 else 269 _highlightsByMask &= ~NSChangeGrayCellMask; 270 break; 271 case NSCellLightsByBackground: 272 if (value) 273 _highlightsByMask |= NSChangeBackgroundCellMask; 274 else 275 _highlightsByMask &= ~NSChangeBackgroundCellMask; 276 break; 277 case NSCellLightsByContents: 278 if (value) 279 _highlightsByMask |= NSContentsCellMask; 280 else 281 _highlightsByMask &= ~NSContentsCellMask; 282 break; 283 default: 284 [super setCellAttribute: aParameter to: value]; 285 } 286} 287 288/** <p>Sets the NSButtonCell's font to <var>fontObject</var>. 289 The key equivalent font size is changed to match the <var>fontObject</var> 290 if needed.</p><p>See Also: [NSCell-font] -keyEquivalentFont 291 -setKeyEquivalentFont: -setKeyEquivalentFont:size:</p> 292 */ 293- (void) setFont: (NSFont*)fontObject 294{ 295 int size; 296 297 [super setFont: fontObject]; 298 299 if ((_keyEquivalentFont != nil) && (fontObject != nil) 300 && ((size = [fontObject pointSize]) != [_keyEquivalentFont pointSize])) 301 { 302 [self setKeyEquivalentFont: [_keyEquivalentFont fontName] 303 size: size]; 304 } 305} 306 307/**<p>Sets the NSButtonCell's title to <var>aString</var>.</p> 308 */ 309- (void) setTitle: (NSString*)aString 310{ 311 ASSIGNCOPY(_contents, aString); 312 _cell.contents_is_attributed_string = NO; 313 314 if (_control_view) 315 { 316 if ([_control_view isKindOfClass: [NSControl class]]) 317 { 318 [(NSControl*)_control_view updateCell: self]; 319 } 320 } 321} 322 323/**<p>Sets the NSButtonCell's alternate title ( used when highlighted ) 324 to <var>aString</var> and update the cell if it contains 325 a NSControl view.</p><p>See Also: -alternateTitle</p> 326 */ 327- (void) setAlternateTitle: (NSString*)aString 328{ 329 ASSIGNCOPY(_altContents, aString); 330 331 if (_control_view) 332 { 333 if ([_control_view isKindOfClass: [NSControl class]]) 334 { 335 [(NSControl*)_control_view updateCell: self]; 336 } 337 } 338} 339 340- (NSAttributedString *)attributedAlternateTitle 341{ 342 // TODO 343 NSDictionary *dict; 344 NSAttributedString *attrStr; 345 346 dict = [self _nonAutoreleasedTypingAttributes]; 347 attrStr = [[NSAttributedString alloc] initWithString: [self alternateTitle] 348 attributes: dict]; 349 RELEASE(dict); 350 351 return AUTORELEASE(attrStr); 352} 353 354- (void)setAttributedAlternateTitle:(NSAttributedString *)aString 355{ 356 // TODO 357 [self setAlternateTitle: [aString string]]; 358} 359 360- (NSAttributedString *)attributedTitle 361{ 362 if (_cell.contents_is_attributed_string && 363 nil != _contents) 364 { 365 return (NSAttributedString *)_contents; 366 } 367 else 368 { 369 NSDictionary *dict; 370 NSAttributedString *attrStr; 371 372 dict = [self _nonAutoreleasedTypingAttributes]; 373 attrStr = [[NSAttributedString alloc] initWithString: [self title] 374 attributes: dict]; 375 RELEASE(dict); 376 return AUTORELEASE(attrStr); 377 } 378} 379 380- (void)setAttributedTitle:(NSAttributedString *)aString 381{ 382 ASSIGNCOPY(_contents, aString); 383 _cell.contents_is_attributed_string = YES; 384 385 if (_control_view) 386 { 387 if ([_control_view isKindOfClass: [NSControl class]]) 388 { 389 [(NSControl*)_control_view updateCell: self]; 390 } 391 } 392} 393 394- (void)setTitleWithMnemonic:(NSString *)aString 395{ 396 // TODO 397 [super setTitleWithMnemonic: aString]; 398} 399 400- (NSString *)alternateMnemonic 401{ 402 // TODO 403 return @""; 404} 405 406- (NSUInteger)alternateMnemonicLocation 407{ 408 // TODO 409 return NSNotFound; 410} 411 412- (void)setAlternateMnemonicLocation:(NSUInteger)location 413{ 414 // TODO 415} 416 417- (void)setAlternateTitleWithMnemonic:(NSString *)aString 418{ 419 NSUInteger location = [aString rangeOfString: @"&"].location; 420 421 [self setAlternateTitle: [aString stringByReplacingString: @"&" 422 withString: @""]]; 423 // TODO: We should underline this character 424 [self setAlternateMnemonicLocation: location]; 425} 426 427/**<p>Returns the NSButtonCell's alternate image.</p> 428 <p>See Also: -setAlternateImage:</p> 429 */ 430- (NSImage*) alternateImage 431{ 432 return _altImage; 433} 434 435/** <p>Returns the NSButtonCell's image position. See <ref type="type" 436 id="NSCellImagePosition">NSCellImagePosition</ref> for more information. 437 </p><p>See Also: -setImagePosition:</p> 438 */ 439- (NSCellImagePosition) imagePosition 440{ 441 return _cell.image_position; 442} 443 444- (NSImageScaling) imageScaling 445{ 446 return _imageScaling; 447} 448 449- (void) setImageScaling: (NSImageScaling)scaling 450{ 451 _imageScaling = scaling; 452} 453 454- (void) setImage: (NSImage *)anImage 455{ 456 if (_cell.image_position == NSNoImage) 457 { 458 [self setImagePosition: NSImageOnly]; 459 } 460 461 [super setImage: anImage]; 462} 463 464/**<p>Sets the NSButtonCell's alternate image to <var>anImage</var>.</p> 465 <p>See Also: -alternateImage</p> 466 */ 467- (void) setAlternateImage: (NSImage*)anImage 468{ 469 ASSIGN(_altImage, anImage); 470 471 if (_control_view) 472 { 473 if ([_control_view isKindOfClass: [NSControl class]]) 474 { 475 [(NSControl*)_control_view updateCell: self]; 476 } 477 } 478} 479 480/**<p>Sets the image position. The GNUstep implementation depends only on 481 the image position. If the image position is set to <ref type="type" 482 id="NSCellImagePosition">NSNoImage</ref> then the type is set to 483 <ref type="type" id="NSCellImagePosition">NSTextCellType</ref>, to 484 <ref type="type" id="NSCellImagePosition">NSImageCellType</ref> otherwise 485 </p><p>See Also: -imagePosition</p> 486*/ 487- (void) setImagePosition: (NSCellImagePosition)aPosition 488{ 489 _cell.image_position = aPosition; 490 491 // In the GNUstep NSButtonCell implementation, the cell type depends only on 492 // the image position. 493 /* NOTE: We set the cell type attribute directly here instead of calling 494 NSCell's -setType: method because it may change the title or image of 495 the button cell. This is to make our implementation compatible with 496 the behavior of Mac OS X, which does not change the cell's type and 497 hence does not involve any of the side effects of -setType: either. */ 498 if (_cell.image_position == NSNoImage) 499 { 500 _cell.type = NSTextCellType; 501 } 502 else 503 { 504 _cell.type = NSImageCellType; 505 } 506 507 if (_control_view) 508 { 509 if ([_control_view isKindOfClass: [NSControl class]]) 510 { 511 [(NSControl*)_control_view updateCell: self]; 512 } 513 } 514} 515 516/**<p>Gets the NSButtonCell's <var>delay</var> and the <var>interval</var> 517 parameters used when NSButton sends continouly action messages. 518 By default <var>delay</var> is 0.4 and <var>interval</var> is 0.075.</p> 519 <p>See Also: -setPeriodicDelay:interval: 520 [NSCell-trackMouse:inRect:ofView:untilMouseUp:]</p> 521 */ 522- (void) getPeriodicDelay: (float*)delay interval: (float*)interval 523{ 524 *delay = _delayInterval; 525 *interval = _repeatInterval; 526} 527 528/** <p>Sets the NSButtonCell's <var>delay</var> and <var>interval</var> 529 parameters used when NSButton sends continouly action messages. 530 By default <var>delay</var> is 0.4 and <var>interval</var> is 0.075.</p> 531 <p>See Also: -getPeriodicDelay:interval: 532 [NSCell-trackMouse:inRect:ofView:untilMouseUp:]</p> 533 */ 534- (void) setPeriodicDelay: (float)delay interval: (float)interval 535{ 536 _delayInterval = delay; 537 _repeatInterval = interval; 538} 539 540/**<p>Returns the NSButtonCell's key equivalent. The key equivalent and its 541 modifier mask are used to simulate the click of the button in 542 [NSButton-performKeyEquivalent:]. Returns an empty string if no key 543 equivalent is defined. By default NSButtonCell hasn't key equivalent.</p> 544 <p>See Also: -setKeyEquivalent: [NSButton-performKeyEquivalent:] 545 -keyEquivalentModifierMask [NSButtonCell-keyEquivalent]</p> 546 */ 547- (NSString*) keyEquivalent 548{ 549 if (nil == _keyEquivalent) 550 { 551 return @""; 552 } 553 else 554 { 555 return _keyEquivalent; 556 } 557} 558 559/**<p>Returns the NSFont of the key equivalent.</p> 560 *<p>See Also: -setKeyEquivalentFont:</p> 561 */ 562- (NSFont*) keyEquivalentFont 563{ 564 return _keyEquivalentFont; 565} 566 567/** <p>Returns the modifier mask of the NSButtonCell's key equivalent. 568 The key equivalent and its modifier mask are used to simulate the click 569 of the button in [NSButton-performKeyEquivalent:]. The default mask is 570 0.</p><p>See Also: -setKeyEquivalentModifierMask: 571 -keyEquivalent [NSButton-performKeyEquivalent:]</p> 572 */ 573- (NSUInteger) keyEquivalentModifierMask 574{ 575 return _keyEquivalentModifierMask; 576} 577 578/** <p>Sets the NSButtonCell's key equivalent to <var>key</var>. The key 579 equivalent and its modifier mask are used to simulate the click 580 of the button in [NSButton-performKeyEquivalent:]. By default NSButton 581 hasn't key equivalent.</p><p>See Also: -keyEquivalent 582 -setKeyEquivalentModifierMask: [NSButton-performKeyEquivalent:]</p> 583*/ 584- (void) setKeyEquivalent: (NSString*)key 585{ 586 [[GSTheme theme] setKeyEquivalent: key 587 forButtonCell: self]; 588 ASSIGNCOPY(_keyEquivalent, key); 589} 590 591/** <p>Sets the modifier mask of the NSButtonCell's key equivalent to 592 <var>mask</var>. The key equivalent and its modifier mask are used to 593 simulate the click of the button in [NSButton-performKeyEquivalent:]. 594 By default the mask is 0.</p> 595 <p>See Also: -keyEquivalentModifierMask 596 -setKeyEquivalent: [NSButton-performKeyEquivalent:]</p> 597*/ 598- (void) setKeyEquivalentModifierMask: (NSUInteger)mask 599{ 600 _keyEquivalentModifierMask = mask; 601} 602 603/**<p>Sets the NSFont of the key equivalent to <var>fontObject</var>.</p> 604 *<p>See Also: -keyEquivalentFont -setFont:</p> 605 */ 606- (void) setKeyEquivalentFont: (NSFont*)fontObj 607{ 608 ASSIGN(_keyEquivalentFont, fontObj); 609} 610 611/**<p>Sets the NSFont with size <var>fontSize</var> of the key equivalent 612 to <var>fontName</var>.</p> 613 <p>See Also: -keyEquivalentFont -setKeyEquivalentFont: -setFont:</p> 614 */ 615- (void) setKeyEquivalentFont: (NSString*)fontName size: (float)fontSize 616{ 617 ASSIGN(_keyEquivalentFont, [NSFont fontWithName: fontName size: fontSize]); 618} 619 620/**<p>Returns whether the button cell is transparent.</p> 621 <p>See Also: -setTransparent:</p> 622 */ 623- (BOOL) isTransparent 624{ 625 return _buttoncell_is_transparent; 626} 627 628/**<p>Sets whether the button cell is transparent.</p> 629 <p>See Also: -isTransparent </p> 630 */ 631- (void) setTransparent: (BOOL)flag 632{ 633 _buttoncell_is_transparent = flag; 634} 635 636/**<p>Returns whether the NSButtonCell is opaque. Currently always 637 returns NO</p>*/ 638- (BOOL) isOpaque 639{ 640 // May not be opaque, due to themes 641 return NO; 642 643 // return !_buttoncell_is_transparent && _cell.is_bordered && 644 // _bezel_style == 0; 645} 646 647- (NSBezelStyle) bezelStyle 648{ 649 return _bezel_style; 650} 651 652- (void) setBezelStyle: (NSBezelStyle)bezelStyle 653{ 654 _bezel_style = bezelStyle; 655} 656 657- (BOOL) showsBorderOnlyWhileMouseInside 658{ 659 return _shows_border_only_while_mouse_inside; 660} 661 662- (void) setShowsBorderOnlyWhileMouseInside: (BOOL)show 663{ 664 if (_shows_border_only_while_mouse_inside == show) 665 { 666 return; 667 } 668 669 _shows_border_only_while_mouse_inside = show; 670 // FIXME Switch mouse tracking on 671} 672 673- (NSGradientType) gradientType 674{ 675 return _gradient_type; 676} 677 678- (void) setGradientType: (NSGradientType)gradientType 679{ 680 _gradient_type = gradientType; 681} 682 683- (BOOL) imageDimsWhenDisabled 684{ 685 return _image_dims_when_disabled; 686} 687 688- (void) setImageDimsWhenDisabled:(BOOL)flag 689{ 690 _image_dims_when_disabled = flag; 691} 692 693/**<p>Returns a mask describing how the button cell is highlighted : </p> 694 <p> NSNoCellMask, NSContentsCellMask,NSPushInCellMask,NSChangeGrayCellMask, 695 NSChangeBackgroundCellMask</p> 696 <p>See Also : -setHighlightsBy:</p> 697 */ 698- (NSInteger) highlightsBy 699{ 700 return _highlightsByMask; 701} 702 703/**<p>Sets a mask describing how the button cell is highlighted : </p> 704 <p> NSNoCellMask, NSContentsCellMask,NSPushInCellMask,NSChangeGrayCellMask, 705 NSChangeBackgroundCellMask</p> 706 <p>See Also : -highlightsBy</p> 707 */ 708- (void) setHighlightsBy: (NSInteger)mask 709{ 710 _highlightsByMask = mask; 711} 712 713- (void) setShowsStateBy: (NSInteger)mask 714{ 715 _showAltStateMask = mask; 716} 717 718- (void) setButtonType: (NSButtonType)buttonType 719{ 720 // Don't store the button type anywhere 721 722 switch (buttonType) 723 { 724 case NSMomentaryLightButton: 725 [self setHighlightsBy: NSChangeBackgroundCellMask]; 726 [self setShowsStateBy: NSNoCellMask]; 727 [self setImageDimsWhenDisabled: YES]; 728 break; 729 case NSMomentaryPushInButton: 730 [self setHighlightsBy: NSPushInCellMask | NSChangeGrayCellMask]; 731 [self setShowsStateBy: NSNoCellMask]; 732 [self setImageDimsWhenDisabled: YES]; 733 break; 734 case NSMomentaryChangeButton: 735 [self setHighlightsBy: NSContentsCellMask]; 736 [self setShowsStateBy: NSNoCellMask]; 737 [self setImageDimsWhenDisabled: YES]; 738 break; 739 case NSPushOnPushOffButton: 740 [self setHighlightsBy: NSPushInCellMask | NSChangeGrayCellMask]; 741 [self setShowsStateBy: NSChangeBackgroundCellMask]; 742 [self setImageDimsWhenDisabled: YES]; 743 break; 744 case NSOnOffButton: 745 [self setHighlightsBy: NSChangeBackgroundCellMask]; 746 [self setShowsStateBy: NSChangeBackgroundCellMask]; 747 [self setImageDimsWhenDisabled: YES]; 748 break; 749 case NSToggleButton: 750 [self setHighlightsBy: NSPushInCellMask | NSContentsCellMask]; 751 [self setShowsStateBy: NSContentsCellMask]; 752 [self setImageDimsWhenDisabled: YES]; 753 break; 754 case NSSwitchButton: 755 [self setHighlightsBy: NSContentsCellMask]; 756 [self setShowsStateBy: NSContentsCellMask]; 757 [self setImage: [NSImage imageNamed: @"NSSwitch"]]; 758 [self setAlternateImage: [NSImage imageNamed: @"NSHighlightedSwitch"]]; 759 [self setImagePosition: NSImageLeft]; 760 [self setAlignment: NSLeftTextAlignment]; 761 [self setBordered: NO]; 762 [self setBezeled: NO]; 763 [self setImageDimsWhenDisabled: NO]; 764 break; 765 case NSRadioButton: 766 [self setHighlightsBy: NSContentsCellMask]; 767 [self setShowsStateBy: NSContentsCellMask]; 768 [self setImage: [NSImage imageNamed: @"NSRadioButton"]]; 769 [self setAlternateImage: [NSImage imageNamed: @"NSHighlightedRadioButton"]]; 770 [self setImagePosition: NSImageLeft]; 771 [self setAlignment: NSLeftTextAlignment]; 772 [self setBordered: NO]; 773 [self setBezeled: NO]; 774 [self setImageDimsWhenDisabled: NO]; 775 break; 776 default: 777 NSLog(@"Using unsupported button type %d", buttonType); 778 break; 779 } 780} 781 782- (NSInteger) showsStateBy 783{ 784 return _showAltStateMask; 785} 786 787- (void) setIntValue: (int)anInt 788{ 789 [self setState: (anInt != 0)]; 790} 791 792- (void) setFloatValue: (float)aFloat 793{ 794 [self setState: (aFloat != 0)]; 795} 796 797- (void) setDoubleValue: (double)aDouble 798{ 799 [self setState: (aDouble != 0)]; 800} 801 802- (int) intValue 803{ 804 return _cell.state; 805} 806 807- (float) floatValue 808{ 809 return _cell.state; 810} 811 812- (double) doubleValue 813{ 814 return _cell.state; 815} 816 817- (void) setObjectValue: (id)object 818{ 819 if (object == nil) 820 { 821 [self setState: NSOffState]; 822 } 823 else if ([object respondsToSelector: @selector(intValue)]) 824 { 825 [self setState: [object intValue]]; 826 } 827 else 828 { 829 [self setState: NSOnState]; 830 } 831} 832 833- (id) objectValue 834{ 835 if (_cell.state == NSOffState) 836 { 837 return [NSNumber numberWithBool: NO]; 838 } 839 else if (_cell.state == NSOnState) 840 { 841 return [NSNumber numberWithBool: YES]; 842 } 843 else // NSMixedState 844 { 845 return [NSNumber numberWithInt: -1]; 846 } 847} 848 849- (void) setStringValue: (NSString *)aString 850{ 851 [self setState: ([aString length] != 0)]; 852} 853 854- (NSString *) stringValue 855{ 856 return _cell.state ? @"1" : @""; 857} 858 859- (void) setAttributedStringValue: (NSAttributedString *)attrString 860{ 861 [self setState: ([attrString length] != 0)]; 862} 863 864- (NSAttributedString *) attributedStringValue 865{ 866 return AUTORELEASE([[NSAttributedString alloc] 867 initWithString: [self stringValue]]); 868} 869 870/* 871 * Displaying 872 */ 873- (NSColor*) textColor 874{ 875 if (_cell.is_disabled == YES) 876 return [NSColor disabledControlTextColor]; 877 if ((_cell.state && (_showAltStateMask & NSChangeGrayCellMask)) 878 || (_cell.is_highlighted && (_highlightsByMask & NSChangeGrayCellMask))) 879 return [NSColor selectedControlTextColor]; 880 return [NSColor controlTextColor]; 881} 882 883- (NSColor *) backgroundColor 884{ 885 return _backgroundColor; 886} 887 888- (void) setBackgroundColor: (NSColor *)color 889{ 890 ASSIGN(_backgroundColor, color); 891 892 if (_control_view) 893 { 894 if ([_control_view isKindOfClass: [NSControl class]]) 895 { 896 [(NSControl*)_control_view updateCell: self]; 897 } 898 } 899} 900 901- (GSThemeControlState) themeControlState 902{ 903 unsigned mask; 904 GSThemeControlState buttonState = GSThemeNormalState; 905 906 // set the mask 907 if (_cell.is_highlighted) 908 { 909 mask = _highlightsByMask; 910 if (_cell.state) 911 { 912 mask &= ~_showAltStateMask; 913 } 914 } 915 else if (_cell.state) 916 mask = _showAltStateMask; 917 else 918 mask = NSNoCellMask; 919 920 /* Determine the background color. 921 We draw when there is a border or when highlightsByMask 922 is NSChangeBackgroundCellMask or NSChangeGrayCellMask, 923 as required by our nextstep-like look and feel. */ 924 if (mask & (NSChangeGrayCellMask | NSChangeBackgroundCellMask)) 925 { 926 buttonState = GSThemeHighlightedState; 927 } 928 929 /* Pushed in buttons contents are displaced to the bottom right 1px. */ 930 if (mask & NSPushInCellMask) 931 { 932 buttonState = GSThemeSelectedState; 933 } 934 935 if (_cell.is_disabled && buttonState != GSThemeHighlightedState) 936 { 937 buttonState = GSThemeDisabledState; 938 } 939 940 /* If we are first responder, change to the corresponding 941 first responder state. Note that GSThemeDisabledState 942 doesn't have a first responder variant, currently. */ 943 if (_cell.shows_first_responder 944 && [[[self controlView] window] firstResponder] == [self controlView] 945 && [self controlView] != nil) 946 { 947 if (buttonState == GSThemeSelectedState) 948 buttonState = GSThemeSelectedFirstResponderState; 949 else if (buttonState == GSThemeHighlightedState) 950 buttonState = GSThemeHighlightedFirstResponderState; 951 else if (buttonState == GSThemeNormalState) 952 buttonState = GSThemeFirstResponderState; 953 } 954 955 return buttonState; 956} 957 958- (void) drawBezelWithFrame: (NSRect)cellFrame inView: (NSView *)controlView 959{ 960 GSThemeControlState buttonState = [self themeControlState]; 961 962 [[GSTheme theme] drawButton: cellFrame 963 in: self 964 view: controlView 965 style: _bezel_style 966 state: buttonState]; 967} 968 969- (void) drawImage: (NSImage*)imageToDisplay 970 withFrame: (NSRect)cellFrame 971 inView: (NSView*)controlView 972{ 973 // Draw image 974 if (imageToDisplay != nil) 975 { 976 NSPoint offset; 977 NSRect rect; 978 CGFloat fraction; 979 NSSize size = [self _scaleImageWithSize: [imageToDisplay size] 980 toFitInSize: cellFrame.size 981 scalingType: _imageScaling]; 982 983 /* Calculate an offset from the cellFrame origin */ 984 offset = NSMakePoint((NSWidth(cellFrame) - size.width) / 2.0, 985 (NSHeight(cellFrame) - size.height) / 2.0); 986 987 rect = NSMakeRect(cellFrame.origin.x + offset.x, 988 cellFrame.origin.y + offset.y, 989 size.width, 990 size.height); 991 992 /* Pixel-align */ 993 if (nil != controlView) 994 { 995 rect = [controlView centerScanRect: rect]; 996 } 997 998 /* Draw the image */ 999 1000 fraction = (![self isEnabled] && 1001 [self imageDimsWhenDisabled]) ? 0.5 : 1.0; 1002 1003 [imageToDisplay drawInRect: rect 1004 fromRect: NSZeroRect 1005 operation: NSCompositeSourceOver 1006 fraction: fraction 1007 respectFlipped: YES 1008 hints: nil]; 1009 } 1010} 1011 1012- (NSRect) drawTitle: (NSAttributedString*)titleToDisplay 1013 withFrame: (NSRect)cellFrame 1014 inView: (NSView*)controlView 1015{ 1016 [self _drawAttributedText: titleToDisplay 1017 inFrame: cellFrame]; 1018 1019 return [titleToDisplay 1020 boundingRectWithSize: cellFrame.size 1021 options: NSStringDrawingUsesLineFragmentOrigin]; 1022} 1023 1024// Private helper method overridden in subclasses 1025- (void) _drawBorderAndBackgroundWithFrame: (NSRect)cellFrame 1026 inView: (NSView*)controlView 1027{ 1028 /* The background color is used for borderless cells (the MacOS-X 1029 * documentation of the NSButtonCell -backgroundColor method says 1030 * it's only used for borderless cells). 1031 */ 1032 if (!_cell.is_bordered) 1033 { 1034 NSColor *c = [self backgroundColor]; 1035 1036 if (c != nil) 1037 { 1038 [c set]; 1039 NSRectFill(cellFrame); 1040 } 1041 } 1042 1043 // Draw gradient 1044 if (!_cell.is_highlighted) 1045 { 1046 [[GSTheme theme] drawGradientBorder: _gradient_type 1047 inRect: cellFrame 1048 withClip: NSZeroRect]; 1049 } 1050 1051 // The inside check could also be done via a track rect, but then this would 1052 // only work with specially prepared controls. Therefore we dont use 1053 // _mouse_inside here. 1054 if ((_cell.is_bordered) 1055 && (!_shows_border_only_while_mouse_inside 1056 || [controlView mouse: [[controlView window] mouseLocationOutsideOfEventStream] 1057 inRect: cellFrame])) 1058 { 1059 [self drawBezelWithFrame: cellFrame inView: controlView]; 1060 } 1061} 1062 1063- (void) drawInteriorWithFrame: (NSRect)cellFrame inView: (NSView*)controlView 1064{ 1065 unsigned mask; 1066 NSImage *imageToDisplay; 1067 NSRect imageRect; 1068 NSAttributedString *titleToDisplay; 1069 NSRect titleRect; 1070 NSSize imageSize = {0, 0}; 1071 NSSize titleSize = {0, 0}; 1072 BOOL flippedView = [controlView isFlipped]; 1073 NSCellImagePosition ipos = _cell.image_position; 1074 1075 // transparent buttons never draw 1076 if (_buttoncell_is_transparent) 1077 return; 1078 1079 _control_view = controlView; 1080 1081 cellFrame = [self drawingRectForBounds: cellFrame]; 1082 1083 if (_cell.is_highlighted) 1084 { 1085 mask = _highlightsByMask; 1086 1087 if (_cell.state) 1088 mask &= ~_showAltStateMask; 1089 } 1090 else if (_cell.state) 1091 mask = _showAltStateMask; 1092 else 1093 mask = NSNoCellMask; 1094 1095 /* 1096 * Determine the image and the title that will be 1097 * displayed. If the NSContentsCellMask is set the 1098 * image and title are swapped only if state is 1 or 1099 * if highlighting is set (when a button is pushed it's 1100 * content is changed to the face of reversed state). 1101 */ 1102 if (mask & NSContentsCellMask) 1103 { 1104 imageToDisplay = _altImage; 1105 if (!imageToDisplay) 1106 { 1107 imageToDisplay = _cell_image; 1108 } 1109 titleToDisplay = [self attributedAlternateTitle]; 1110 if (titleToDisplay == nil || [titleToDisplay length] == 0) 1111 { 1112 titleToDisplay = [self attributedTitle]; 1113 } 1114 } 1115 else 1116 { 1117 imageToDisplay = _cell_image; 1118 titleToDisplay = [self attributedTitle]; 1119 } 1120 1121 if (imageToDisplay && ipos != NSNoImage) 1122 { 1123 imageSize = [imageToDisplay size]; 1124 } 1125 else 1126 { 1127 // When there is no image to display, ignore it in the calculations 1128 imageToDisplay = nil; 1129 ipos = NSNoImage; 1130 } 1131 1132 if (titleToDisplay && ipos != NSImageOnly) 1133 { 1134 titleSize = [titleToDisplay size]; 1135 } 1136 else 1137 { 1138 // When there is no text to display, ignore it in the calculations 1139 titleToDisplay = nil; 1140 ipos = NSImageOnly; 1141 } 1142 1143 if (flippedView == YES) 1144 { 1145 if (ipos == NSImageAbove) 1146 { 1147 ipos = NSImageBelow; 1148 } 1149 else if (ipos == NSImageBelow) 1150 { 1151 ipos = NSImageAbove; 1152 } 1153 } 1154 1155 /* 1156 The size calculations here should be changed very carefully, and _must_ be 1157 kept in sync with -cellSize. Changing the calculations to require more 1158 space isn't OK; this breaks interfaces designed using the old sizes by 1159 clipping away parts of the title. 1160 1161 The current size calculations ensure that for bordered or bezeled cells, 1162 there's always at least a three point margin between the size returned by 1163 -cellSize and the minimum size required not to clip text. (In other words, 1164 the text can become three points wider (due to eg. font mismatches) before 1165 you lose the last character.) 1166 */ 1167 switch (ipos) 1168 { 1169 default: 1170 case NSNoImage: 1171 imageToDisplay = nil; 1172 titleRect = cellFrame; 1173 imageRect = NSZeroRect; 1174 if (titleSize.width + 6 <= titleRect.size.width) 1175 { 1176 titleRect.origin.x += 3; 1177 titleRect.size.width -= 6; 1178 } 1179 break; 1180 1181 case NSImageOnly: 1182 titleToDisplay = nil; 1183 imageRect = cellFrame; 1184 titleRect = NSZeroRect; 1185 break; 1186 1187 case NSImageLeft: 1188 imageRect.origin = cellFrame.origin; 1189 imageRect.size.width = imageSize.width; 1190 imageRect.size.height = cellFrame.size.height; 1191 if (_cell.is_bordered || _cell.is_bezeled) 1192 { 1193 imageRect.origin.x += 3; 1194 } 1195 titleRect = imageRect; 1196 titleRect.origin.x += imageSize.width + GSCellTextImageXDist; 1197 titleRect.size.width = NSMaxX(cellFrame) - titleRect.origin.x; 1198 if (titleSize.width + 3 <= titleRect.size.width) 1199 { 1200 titleRect.size.width -= 3; 1201 } 1202 break; 1203 1204 case NSImageRight: 1205 imageRect.origin.x = NSMaxX(cellFrame) - imageSize.width; 1206 imageRect.origin.y = cellFrame.origin.y; 1207 imageRect.size.width = imageSize.width; 1208 imageRect.size.height = cellFrame.size.height; 1209 if (_cell.is_bordered || _cell.is_bezeled) 1210 { 1211 imageRect.origin.x -= 3; 1212 } 1213 titleRect.origin = cellFrame.origin; 1214 titleRect.size.width = imageRect.origin.x - titleRect.origin.x 1215 - GSCellTextImageXDist; 1216 titleRect.size.height = cellFrame.size.height; 1217 if (titleSize.width + 3 <= titleRect.size.width) 1218 { 1219 titleRect.origin.x += 3; 1220 titleRect.size.width -= 3; 1221 } 1222 break; 1223 1224 case NSImageAbove: 1225 /* 1226 * In this case, imageRect is all the space we can allocate 1227 * above the text. 1228 * The drawing code below will then center the image in imageRect. 1229 */ 1230 titleRect.origin = cellFrame.origin; 1231 titleRect.size.width = cellFrame.size.width; 1232 titleRect.size.height = titleSize.height; 1233 if (_cell.is_bordered || _cell.is_bezeled) 1234 { 1235 titleRect.origin.y += 3; 1236 } 1237 1238 imageRect.origin.x = cellFrame.origin.x; 1239 imageRect.origin.y = NSMaxY(titleRect) + GSCellTextImageYDist; 1240 imageRect.size.width = cellFrame.size.width; 1241 imageRect.size.height = NSMaxY(cellFrame) - imageRect.origin.y; 1242 1243 if (_cell.is_bordered || _cell.is_bezeled) 1244 { 1245 imageRect.size.height -= 3; 1246 } 1247 if (titleSize.width + 6 <= titleRect.size.width) 1248 { 1249 titleRect.origin.x += 3; 1250 titleRect.size.width -= 6; 1251 } 1252 break; 1253 1254 case NSImageBelow: 1255 /* 1256 * In this case, imageRect is all the space we can allocate 1257 * below the text. 1258 * The drawing code below will then center the image in imageRect. 1259 */ 1260 titleRect.origin.x = cellFrame.origin.x; 1261 titleRect.origin.y = NSMaxY(cellFrame) - titleSize.height; 1262 titleRect.size.width = cellFrame.size.width; 1263 titleRect.size.height = titleSize.height; 1264 if (_cell.is_bordered || _cell.is_bezeled) 1265 { 1266 titleRect.origin.y -= 3; 1267 } 1268 1269 imageRect.origin.x = cellFrame.origin.x; 1270 imageRect.origin.y = cellFrame.origin.y; 1271 imageRect.size.width = cellFrame.size.width; 1272 imageRect.size.height 1273 = titleRect.origin.y - GSCellTextImageYDist - imageRect.origin.y; 1274 1275 if (_cell.is_bordered || _cell.is_bezeled) 1276 { 1277 imageRect.origin.y += 3; 1278 imageRect.size.height -= 3; 1279 } 1280 if (titleSize.width + 6 <= titleRect.size.width) 1281 { 1282 titleRect.origin.x += 3; 1283 titleRect.size.width -= 6; 1284 } 1285 break; 1286 1287 case NSImageOverlaps: 1288 imageRect = cellFrame; 1289 titleRect = cellFrame; 1290 if (titleSize.width + 6 <= titleRect.size.width) 1291 { 1292 titleRect.origin.x += 3; 1293 titleRect.size.width -= 6; 1294 } 1295 break; 1296 } 1297 1298 // Draw image 1299 if (imageToDisplay != nil) 1300 { 1301 [self drawImage: imageToDisplay 1302 withFrame: imageRect 1303 inView: controlView]; 1304 } 1305 1306 // Draw title 1307 if (titleToDisplay != nil) 1308 { 1309 [self drawTitle: titleToDisplay withFrame: titleRect inView: controlView]; 1310 } 1311} 1312 1313- (NSSize) cellSize 1314{ 1315 NSSize s; 1316 GSThemeMargins border; 1317 unsigned mask; 1318 NSImage *imageToDisplay; 1319 NSAttributedString *titleToDisplay; 1320 NSSize imageSize = NSZeroSize; 1321 NSSize titleSize = NSZeroSize; 1322 1323 /* The size calculations here must be kept in sync with 1324 -drawInteriorWithFrame. */ 1325 1326 if (_cell.is_highlighted) 1327 { 1328 mask = _highlightsByMask; 1329 1330 if (_cell.state) 1331 mask &= ~_showAltStateMask; 1332 } 1333 else if (_cell.state) 1334 mask = _showAltStateMask; 1335 else 1336 mask = NSNoCellMask; 1337 1338 if (mask & NSContentsCellMask) 1339 { 1340 imageToDisplay = _altImage; 1341 if (!imageToDisplay) 1342 { 1343 imageToDisplay = _cell_image; 1344 } 1345 titleToDisplay = [self attributedAlternateTitle]; 1346 if (titleToDisplay == nil || [titleToDisplay length] == 0) 1347 { 1348 titleToDisplay = [self attributedTitle]; 1349 } 1350 } 1351 else 1352 { 1353 imageToDisplay = _cell_image; 1354 titleToDisplay = [self attributedTitle]; 1355 } 1356 1357 if (imageToDisplay) 1358 { 1359 imageSize = [imageToDisplay size]; 1360 } 1361 1362 if (titleToDisplay != nil) 1363 { 1364 titleSize = [titleToDisplay size]; 1365 } 1366 1367 switch (_cell.image_position) 1368 { 1369 default: 1370 case NSNoImage: 1371 s = titleSize; 1372 break; 1373 1374 case NSImageOnly: 1375 s = imageSize; 1376 break; 1377 1378 case NSImageLeft: 1379 case NSImageRight: 1380 s.width = imageSize.width + titleSize.width + GSCellTextImageXDist; 1381 s.height = MAX(imageSize.height, titleSize.height); 1382 break; 1383 1384 case NSImageBelow: 1385 case NSImageAbove: 1386 s.width = MAX(imageSize.width, titleSize.width); 1387 s.height = imageSize.height + titleSize.height + GSCellTextImageYDist; 1388 break; 1389 1390 case NSImageOverlaps: 1391 s.width = MAX(imageSize.width, titleSize.width); 1392 s.height = MAX(imageSize.height, titleSize.height); 1393 break; 1394 } 1395 1396 // Get border size 1397 if (_cell.is_bordered) 1398 { 1399 GSThemeControlState buttonState = GSThemeNormalState; 1400 1401 /* Determine the background color. 1402 We draw when there is a border or when highlightsByMask 1403 is NSChangeBackgroundCellMask or NSChangeGrayCellMask, 1404 as required by our nextstep-like look and feel. */ 1405 if (mask & (NSChangeGrayCellMask | NSChangeBackgroundCellMask)) 1406 { 1407 buttonState = GSThemeHighlightedState; 1408 } 1409 1410 /* Pushed in buttons contents are displaced to the bottom right 1px. */ 1411 if (mask & NSPushInCellMask) 1412 { 1413 buttonState = GSThemeSelectedState; 1414 } 1415 1416 border = [[GSTheme theme] buttonMarginsForCell: self 1417 style: _bezel_style 1418 state: buttonState]; 1419 } 1420 else 1421 { 1422 border.left = 0; 1423 border.top = 0; 1424 border.right = 0; 1425 border.bottom = 0; 1426 } 1427 1428 /* Add an additional 6 pixels horizontally so that the text is not 1429 * too near the boundaries of the button. Without them, autosized 1430 * buttons look too tiny and crammed. This might be made 1431 * configurable by the theme, but most likely only because themes 1432 * might want to have even more space here (to make buttons more 1433 * clear and readable) rather than less! Eg. Apple by default has 1434 * huge amounts of empty space between the text and the borders of 1435 * their push buttons. 1436 */ 1437 if ((_cell.is_bordered && (_cell.image_position != NSImageOnly)) 1438 || _cell.is_bezeled) 1439 { 1440 border.left += 6; 1441 border.right += 6; 1442 } 1443 1444 // Add border size 1445 s.width += border.left + border.right; 1446 s.height += border.top + border.bottom; 1447 1448 return s; 1449} 1450 1451- (NSRect) drawingRectForBounds: (NSRect)theRect 1452{ 1453 if (_cell.is_bordered) 1454 { 1455 GSThemeMargins border; 1456 unsigned mask; 1457 GSThemeControlState buttonState = GSThemeNormalState; 1458 NSRect interiorFrame; 1459 1460 if (_cell.is_highlighted) 1461 { 1462 mask = _highlightsByMask; 1463 1464 if (_cell.state) 1465 mask &= ~_showAltStateMask; 1466 } 1467 else if (_cell.state) 1468 mask = _showAltStateMask; 1469 else 1470 mask = NSNoCellMask; 1471 1472 /* Determine the background color. 1473 We draw when there is a border or when highlightsByMask 1474 is NSChangeBackgroundCellMask or NSChangeGrayCellMask, 1475 as required by our nextstep-like look and feel. */ 1476 if (mask & (NSChangeGrayCellMask | NSChangeBackgroundCellMask)) 1477 { 1478 buttonState = GSThemeHighlightedState; 1479 } 1480 1481 if (mask & NSPushInCellMask) 1482 { 1483 buttonState = GSThemeSelectedState; 1484 } 1485 1486 border = [[GSTheme theme] buttonMarginsForCell: self 1487 style: _bezel_style 1488 state: buttonState]; 1489 1490 interiorFrame = theRect; 1491 interiorFrame.origin.x += border.left; 1492 interiorFrame.size.width -= border.left + border.right; 1493 interiorFrame.origin.y += ([_control_view isFlipped] ? 1494 border.top : border.bottom); 1495 interiorFrame.size.height -= border.bottom + border.top; 1496 1497 /* Pushed in buttons contents are displaced to the bottom right 1px. */ 1498 if (mask & NSPushInCellMask) 1499 { 1500 interiorFrame = NSOffsetRect(interiorFrame, 1.0, 1501 [_control_view isFlipped] ? 1.0 : -1.0); 1502 } 1503 return interiorFrame; 1504 } 1505 else 1506 { 1507 return theRect; 1508 } 1509} 1510 1511- (void) setSound: (NSSound *)aSound 1512{ 1513 ASSIGN(_sound, aSound); 1514} 1515 1516- (NSSound *) sound 1517{ 1518 return _sound; 1519} 1520 1521- (void) mouseEntered: (NSEvent *)event 1522{ 1523 _mouse_inside = YES; 1524 [(NSView *)[event userData] setNeedsDisplay: YES]; 1525} 1526 1527- (void) mouseExited: (NSEvent *)event 1528{ 1529 _mouse_inside = NO; 1530 [(NSView *)[event userData] setNeedsDisplay: YES]; 1531} 1532 1533/**Simulates a single mouse click on the button cell. This method overrides the 1534 cell method performClickWithFrame:inView: to add the possibility to 1535 play a sound associated with the click. 1536 */ 1537- (void) performClickWithFrame: (NSRect)cellFrame inView: (NSView *)controlView 1538{ 1539 if (_sound != nil) 1540 { 1541 [_sound play]; 1542 } 1543 1544 [super performClickWithFrame: cellFrame inView: controlView]; 1545} 1546 1547/* 1548 * Comparing to Another NSButtonCell 1549 */ 1550- (NSComparisonResult) compare: (id)otherCell 1551{ 1552 if ([otherCell isKindOfClass: [NSButtonCell class]] == NO) 1553 { 1554 [NSException raise: NSBadComparisonException 1555 format: @"NSButtonCell comparison with non-NSButtonCell"]; 1556 } 1557 return [super compare: otherCell]; 1558} 1559 1560/* 1561 * NSCopying protocol 1562 */ 1563- (id) copyWithZone: (NSZone*)zone 1564{ 1565 NSButtonCell *c = [super copyWithZone: zone]; 1566 1567 c->_altContents = [_altContents copyWithZone: zone]; 1568 _altImage = TEST_RETAIN(_altImage); 1569 _keyEquivalent = TEST_RETAIN(_keyEquivalent); 1570 _keyEquivalentFont = TEST_RETAIN(_keyEquivalentFont); 1571 _sound = TEST_RETAIN(_sound); 1572 _backgroundColor = TEST_RETAIN(_backgroundColor); 1573 1574 return c; 1575} 1576 1577/* 1578 * NSCoding protocol 1579 */ 1580- (void) encodeWithCoder: (NSCoder*)aCoder 1581{ 1582 BOOL tmp; 1583 1584 [super encodeWithCoder: aCoder]; 1585 if ([aCoder allowsKeyedCoding]) 1586 { 1587 GSButtonCellFlags buttonCellFlags; 1588 unsigned int bFlags = 0; 1589 unsigned int bFlags2 = 0; 1590 NSImage *image = [self image]; 1591 NSButtonImageSource *bi = nil; 1592 1593 if ([[self keyEquivalent] length] > 0) 1594 { 1595 [aCoder encodeObject: [self keyEquivalent] forKey: @"NSKeyEquivalent"]; 1596 } 1597 if ([self image] != nil) 1598 { 1599 [aCoder encodeObject: [self image] forKey: @"NSNormalImage"]; 1600 } 1601 if ([[self alternateTitle] length] > 0) 1602 { 1603 [aCoder encodeObject: [self alternateTitle] forKey: @"NSAlternateContents"]; 1604 } 1605 1606 buttonCellFlags.useButtonImageSource = (([NSImage imageNamed: @"NSSwitch"] == image) || 1607 ([NSImage imageNamed: @"NSRadioButton"] == image)); 1608 buttonCellFlags.isTransparent = [self isTransparent]; 1609 buttonCellFlags.isBordered = [self isBordered]; 1610 buttonCellFlags.imageDoesOverlap = 1611 (_cell.image_position == NSImageOverlaps) 1612 || (_cell.image_position == NSImageOnly); 1613 buttonCellFlags.isHorizontal = (_cell.image_position == NSImageLeft) 1614 || (_cell.image_position == NSImageRight); 1615 buttonCellFlags.isBottomOrLeft = (_cell.image_position == NSImageLeft) 1616 || (_cell.image_position == NSImageBelow); 1617 buttonCellFlags.isImageAndText = (image != nil) 1618 && (_cell.image_position != NSImageOnly); 1619 buttonCellFlags.hasKeyEquiv = ([self keyEquivalent] != nil); 1620 1621 // cell attributes... 1622 buttonCellFlags.isPushin = [self cellAttribute: NSPushInCell]; 1623 buttonCellFlags.highlightByBackground = [self cellAttribute: NSCellLightsByBackground]; 1624 buttonCellFlags.highlightByContents = [self cellAttribute: NSCellLightsByContents]; 1625 buttonCellFlags.highlightByGray = [self cellAttribute: NSCellLightsByGray]; 1626 buttonCellFlags.changeBackground = [self cellAttribute: NSChangeBackgroundCell]; 1627 buttonCellFlags.changeContents = [self cellAttribute: NSCellChangesContents]; 1628 buttonCellFlags.changeGray = [self cellAttribute: NSChangeGrayCell]; 1629 1630 // set these to zero... 1631 buttonCellFlags.inset = 0; 1632 buttonCellFlags.doesNotDimImage = 0; 1633 buttonCellFlags.gradient = 0; 1634 buttonCellFlags.unused2 = 0; 1635 buttonCellFlags.lastState = 0; 1636 buttonCellFlags.isImageSizeDiff = 0; 1637 buttonCellFlags.drawing = 0; 1638 1639 memcpy((void *)&bFlags, (void *)&buttonCellFlags,sizeof(unsigned int)); 1640 [aCoder encodeInt: bFlags forKey: @"NSButtonFlags"]; 1641 1642 // style and border. 1643 bFlags2 |= [self showsBorderOnlyWhileMouseInside] ? 0x8 : 0; 1644 bFlags2 |= (([self bezelStyle] & 0x7) | (([self bezelStyle] & 0x18) << 2)); 1645 bFlags2 |= [self keyEquivalentModifierMask] << 8; 1646 1647 switch ([self imageScaling]) 1648 { 1649 case NSImageScaleProportionallyDown: 1650 bFlags2 |= (2 << 6); 1651 break; 1652 case NSImageScaleAxesIndependently: 1653 bFlags2 |= (3 << 6); 1654 break; 1655 case NSImageScaleNone: 1656 default: 1657 break; 1658 case NSImageScaleProportionallyUpOrDown: 1659 bFlags2 |= (1 << 6); 1660 break; 1661 } 1662 1663 [aCoder encodeInt: bFlags2 forKey: @"NSButtonFlags2"]; 1664 1665 // alternate image encoding... 1666 if (image != nil) 1667 { 1668 if ([image isKindOfClass: [NSImage class]] && buttonCellFlags.useButtonImageSource) 1669 { 1670 if ([NSImage imageNamed: @"NSSwitch"] == image) 1671 { 1672 bi = [[NSButtonImageSource alloc] initWithImageNamed: @"NSHighlightedSwitch"]; 1673 } 1674 else if ([NSImage imageNamed: @"NSRadioButton"] == image) 1675 { 1676 bi = [[NSButtonImageSource alloc] initWithImageNamed: @"NSHighlightedRadioButton"]; 1677 } 1678 } 1679 } 1680 1681 // encode button image source, if it exists... 1682 if (bi != nil) 1683 { 1684 [aCoder encodeObject: bi forKey: @"NSAlternateImage"]; 1685 RELEASE(bi); 1686 } 1687 else if (_altImage != nil) 1688 { 1689 [aCoder encodeObject: _altImage forKey: @"NSAlternateImage"]; 1690 } 1691 1692 // repeat and delay 1693 [aCoder encodeInt: (int)_delayInterval forKey: @"NSPeriodicDelay"]; 1694 [aCoder encodeInt: (int)_repeatInterval forKey: @"NSPeriodicInterval"]; 1695 } 1696 else 1697 { 1698 [aCoder encodeObject: _keyEquivalent]; 1699 [aCoder encodeObject: _keyEquivalentFont]; 1700 [aCoder encodeObject: _altContents]; 1701 [aCoder encodeObject: _altImage]; 1702 tmp = _buttoncell_is_transparent; 1703 [aCoder encodeValueOfObjCType: @encode(BOOL) 1704 at: &tmp]; 1705 1706 if([NSButtonCell version] <= 2) 1707 { 1708 unsigned int ke = _keyEquivalentModifierMask << 16; 1709 [aCoder encodeValueOfObjCType: @encode(unsigned int) 1710 at: &ke]; 1711 } 1712 else 1713 { 1714 [aCoder encodeValueOfObjCType: @encode(unsigned int) 1715 at: &_keyEquivalentModifierMask]; 1716 } 1717 1718 [aCoder encodeValueOfObjCType: @encode(unsigned int) 1719 at: &_highlightsByMask]; 1720 [aCoder encodeValueOfObjCType: @encode(unsigned int) 1721 at: &_showAltStateMask]; 1722 1723 if([NSButtonCell version] >= 2) 1724 { 1725 [aCoder encodeObject: _sound]; 1726 [aCoder encodeObject: _backgroundColor]; 1727 [aCoder encodeValueOfObjCType: @encode(float) 1728 at: &_delayInterval]; 1729 [aCoder encodeValueOfObjCType: @encode(float) 1730 at: &_repeatInterval]; 1731 [aCoder encodeValueOfObjCType: @encode(unsigned int) 1732 at: &_bezel_style]; 1733 [aCoder encodeValueOfObjCType: @encode(unsigned int) 1734 at: &_gradient_type]; 1735 tmp = _image_dims_when_disabled; 1736 [aCoder encodeValueOfObjCType: @encode(BOOL) 1737 at: &tmp]; 1738 tmp = _shows_border_only_while_mouse_inside; 1739 [aCoder encodeValueOfObjCType: @encode(BOOL) 1740 at: &tmp]; 1741 } 1742 } 1743} 1744 1745- (id) initWithCoder: (NSCoder*)aDecoder 1746{ 1747 self = [super initWithCoder: aDecoder]; 1748 if (!self) 1749 return nil; 1750 1751 if ([aDecoder allowsKeyedCoding]) 1752 { 1753 int delay = 0; 1754 int interval = 0; 1755 // NSControl *control = [aDecoder decodeObjectForKey: @"NSControlView"]; 1756 1757 if ([aDecoder containsValueForKey: @"NSKeyEquivalent"]) 1758 { 1759 [self setKeyEquivalent: [aDecoder decodeObjectForKey: @"NSKeyEquivalent"]]; 1760 } 1761 if ([aDecoder containsValueForKey: @"NSNormalImage"]) 1762 { 1763 [self setImage: [aDecoder decodeObjectForKey: @"NSNormalImage"]]; 1764 } 1765 if ([aDecoder containsValueForKey: @"NSAlternateContents"]) 1766 { 1767 [self setAlternateTitle: [aDecoder decodeObjectForKey: @"NSAlternateContents"]]; 1768 } 1769 if ([aDecoder containsValueForKey: @"NSButtonFlags"]) 1770 { 1771 unsigned int bFlags = [aDecoder decodeIntForKey: @"NSButtonFlags"]; 1772 GSButtonCellFlags buttonCellFlags; 1773 memcpy((void *)&buttonCellFlags,(void *)&bFlags,sizeof(struct _GSButtonCellFlags)); 1774 1775 [self setTransparent: buttonCellFlags.isTransparent]; 1776 [self setBordered: buttonCellFlags.isBordered]; 1777 1778 [self setCellAttribute: NSPushInCell 1779 to: buttonCellFlags.isPushin]; 1780 [self setCellAttribute: NSCellLightsByBackground 1781 to: buttonCellFlags.highlightByBackground]; 1782 [self setCellAttribute: NSCellLightsByContents 1783 to: buttonCellFlags.highlightByContents]; 1784 [self setCellAttribute: NSCellLightsByGray 1785 to: buttonCellFlags.highlightByGray]; 1786 [self setCellAttribute: NSChangeBackgroundCell 1787 to: buttonCellFlags.changeBackground]; 1788 [self setCellAttribute: NSCellChangesContents 1789 to: buttonCellFlags.changeContents]; 1790 [self setCellAttribute: NSChangeGrayCell 1791 to: buttonCellFlags.changeGray]; 1792 1793 if (buttonCellFlags.imageDoesOverlap) 1794 if (buttonCellFlags.isImageAndText) 1795 [self setImagePosition: NSImageOverlaps]; 1796 else 1797 [self setImagePosition: NSImageOnly]; 1798 else if (buttonCellFlags.isImageAndText) 1799 if (buttonCellFlags.isHorizontal) 1800 if (buttonCellFlags.isBottomOrLeft) 1801 [self setImagePosition: NSImageLeft]; 1802 else 1803 [self setImagePosition: NSImageRight]; 1804 else 1805 if (buttonCellFlags.isBottomOrLeft) 1806 [self setImagePosition: NSImageBelow]; 1807 else 1808 [self setImagePosition: NSImageAbove]; 1809 else 1810 [self setImagePosition: NSNoImage]; 1811 } 1812 if ([aDecoder containsValueForKey: @"NSButtonFlags2"]) 1813 { 1814 NSUInteger imageScale; 1815 int bFlags2; 1816 1817 bFlags2 = [aDecoder decodeIntForKey: @"NSButtonFlags2"]; 1818 [self setShowsBorderOnlyWhileMouseInside: (bFlags2 & 0x8)]; 1819 [self setBezelStyle: (bFlags2 & 0x7) | ((bFlags2 & 0x20) >> 2)]; 1820 [self setKeyEquivalentModifierMask: ((bFlags2 >> 8) & 1821 NSDeviceIndependentModifierFlagsMask)]; 1822 1823 switch ((bFlags2 >> 6) & 3) 1824 { 1825 case 2: 1826 imageScale = NSImageScaleProportionallyDown; 1827 break; 1828 case 3: 1829 imageScale = NSImageScaleAxesIndependently; 1830 break; 1831 case 0: 1832 default: 1833 imageScale = NSImageScaleNone; 1834 break; 1835 case 1: 1836 imageScale = NSImageScaleProportionallyUpOrDown; 1837 break; 1838 } 1839 [self setImageScaling: imageScale]; 1840 } 1841 if ([aDecoder containsValueForKey: @"NSAlternateImage"]) 1842 { 1843 id image; 1844 1845 // 1846 // NOTE: Okay... this is a humongous kludge. It seems as though 1847 // Cocoa is doing something very odd here. It doesn't seem to 1848 // encode system images for buttons normally, if it is using 1849 // images at all. Until I figure out what, this will stay. 1850 // Danger, Will Robinson! :) 1851 // 1852 image = [aDecoder decodeObjectForKey: @"NSAlternateImage"]; 1853 if ([image isKindOfClass: [NSImage class]]) 1854 { 1855 if ([NSImage imageNamed: @"NSSwitch"] == image) 1856 { 1857 image = [NSImage imageNamed: @"NSHighlightedSwitch"]; 1858 if ([self image] == nil) 1859 { 1860 [self setImage: [NSImage imageNamed: @"NSSwitch"]]; 1861 } 1862 } 1863 else if ([NSImage imageNamed: @"NSRadioButton"] == image) 1864 { 1865 image = [NSImage imageNamed: @"NSHighlightedRadioButton"]; 1866 if ([self image] == nil) 1867 { 1868 [self setImage: [NSImage imageNamed: @"NSRadioButton"]]; 1869 } 1870 } 1871 1872 [self setAlternateImage: image]; 1873 } 1874 } 1875 if ([aDecoder containsValueForKey: @"NSPeriodicDelay"]) 1876 { 1877 delay = [aDecoder decodeIntForKey: @"NSPeriodicDelay"]; 1878 } 1879 if ([aDecoder containsValueForKey: @"NSPeriodicInterval"]) 1880 { 1881 interval = [aDecoder decodeIntForKey: @"NSPeriodicInterval"]; 1882 } 1883 [self setPeriodicDelay: delay interval: interval]; 1884 } 1885 else 1886 { 1887 BOOL tmp; 1888 int version = [aDecoder versionForClassName: @"NSButtonCell"]; 1889 NSString *key = nil; 1890 1891 [aDecoder decodeValueOfObjCType: @encode(id) at: &key]; 1892 [self setKeyEquivalent: key]; // Set the key equivalent... 1893 1894 [aDecoder decodeValueOfObjCType: @encode(id) at: &_keyEquivalentFont]; 1895 [aDecoder decodeValueOfObjCType: @encode(id) at: &_altContents]; 1896 [aDecoder decodeValueOfObjCType: @encode(id) at: &_altImage]; 1897 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &tmp]; 1898 _buttoncell_is_transparent = tmp; 1899 [aDecoder decodeValueOfObjCType: @encode(unsigned int) 1900 at: &_keyEquivalentModifierMask]; 1901 if (version <= 2) 1902 { 1903 _keyEquivalentModifierMask = _keyEquivalentModifierMask << 16; 1904 } 1905 [aDecoder decodeValueOfObjCType: @encode(unsigned int) 1906 at: &_highlightsByMask]; 1907 [aDecoder decodeValueOfObjCType: @encode(unsigned int) 1908 at: &_showAltStateMask]; 1909 1910 if (version >= 2) 1911 { 1912 [aDecoder decodeValueOfObjCType: @encode(id) at: &_sound]; 1913 [aDecoder decodeValueOfObjCType: @encode(id) at: &_backgroundColor]; 1914 [aDecoder decodeValueOfObjCType: @encode(float) at: &_delayInterval]; 1915 [aDecoder decodeValueOfObjCType: @encode(float) at: &_repeatInterval]; 1916 [aDecoder decodeValueOfObjCType: @encode(unsigned int) 1917 at: &_bezel_style]; 1918 [aDecoder decodeValueOfObjCType: @encode(unsigned int) 1919 at: &_gradient_type]; 1920 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &tmp]; 1921 _image_dims_when_disabled = tmp; 1922 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &tmp]; 1923 _shows_border_only_while_mouse_inside = tmp; 1924 } 1925 // Not encoded in non-keyed archive 1926 _imageScaling = NSImageScaleNone; 1927 } 1928 1929 // Hack to correct a Gorm problem, there "\n" is used instead of "\r". 1930 if ([_keyEquivalent isEqualToString: @"\n" ]) 1931 { 1932 [self setKeyEquivalent: @"\r"]; 1933 } 1934 1935 return self; 1936} 1937 1938@end 1939