![]() |
API 0.9.5
|
00001 /* 00002 * CPButton.j 00003 * AppKit 00004 * 00005 * Created by Francisco Tolmasky. 00006 * Copyright 2008, 280 North, Inc. 00007 * 00008 * This library is free software; you can redistribute it and/or 00009 * modify it under the terms of the GNU Lesser General Public 00010 * License as published by the Free Software Foundation; either 00011 * version 2.1 of the License, or (at your option) any later version. 00012 * 00013 * This library is distributed in the hope that it will be useful, 00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 * Lesser General Public License for more details. 00017 * 00018 * You should have received a copy of the GNU Lesser General Public 00019 * License along with this library; if not, write to the Free Software 00020 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00021 */ 00022 00023 00024 00025 00026 /* @group CPBezelStyle */ 00027 00028 CPRoundedBezelStyle = 1; 00029 CPRegularSquareBezelStyle = 2; 00030 CPThickSquareBezelStyle = 3; 00031 CPThickerSquareBezelStyle = 4; 00032 CPDisclosureBezelStyle = 5; 00033 CPShadowlessSquareBezelStyle = 6; 00034 CPCircularBezelStyle = 7; 00035 CPTexturedSquareBezelStyle = 8; 00036 CPHelpButtonBezelStyle = 9; 00037 CPSmallSquareBezelStyle = 10; 00038 CPTexturedRoundedBezelStyle = 11; 00039 CPRoundRectBezelStyle = 12; 00040 CPRecessedBezelStyle = 13; 00041 CPRoundedDisclosureBezelStyle = 14; 00042 CPHUDBezelStyle = -1; 00043 00044 00045 /* @group CPButtonType */ 00046 CPMomentaryLightButton = 0; 00047 CPPushOnPushOffButton = 1; 00048 CPToggleButton = 2; 00049 CPSwitchButton = 3; // Deprecated, use CPCheckBox instead. 00050 CPRadioButton = 4; // Deprecated, use CPRadio instead. 00051 CPMomentaryChangeButton = 5; 00052 CPOnOffButton = 6; 00053 CPMomentaryPushInButton = 7; 00054 CPMomentaryPushButton = 0; 00055 CPMomentaryLight = 7; 00056 00057 CPNoButtonMask = 0; 00058 CPContentsButtonMask = 1; 00059 CPPushInButtonMask = 2; 00060 CPGrayButtonMask = 4; 00061 CPBackgroundButtonMask = 8; 00062 00063 CPNoCellMask = CPNoButtonMask; 00064 CPContentsCellMask = CPContentsButtonMask; 00065 CPPushInCellMask = CPPushInButtonMask; 00066 CPChangeGrayCellMask = CPGrayButtonMask; 00067 CPChangeBackgroundCellMask = CPBackgroundButtonMask; 00068 00069 CPButtonStateMixed = CPThemeState("mixed"); 00070 CPButtonStateBezelStyleRounded = CPThemeState("rounded"); 00071 00072 // add all future correspondance between bezel styles and theme state here. 00073 var CPButtonBezelStyleStateMap = [CPDictionary dictionaryWithObjects:[CPButtonStateBezelStyleRounded, nil] 00074 forKeys:[CPRoundedBezelStyle, CPRoundRectBezelStyle]]; 00075 00076 00077 CPButtonDefaultHeight = 24.0; 00078 CPButtonImageOffset = 3.0; 00079 00088 @implementation CPButton : CPControl 00089 { 00090 BOOL _allowsMixedState; 00091 00092 CPString _title; 00093 CPString _alternateTitle; 00094 00095 CPInteger _showsStateBy; 00096 CPInteger _highlightsBy; 00097 BOOL _imageDimsWhenDisabled; 00098 00099 // NS-style Display Properties 00100 CPBezelStyle _bezelStyle; 00101 CPControlSize _controlSize; 00102 00103 CPString _keyEquivalent; 00104 unsigned _keyEquivalentModifierMask; 00105 00106 CPTimer _continuousDelayTimer; 00107 CPTimer _continuousTimer; 00108 float _periodicDelay; 00109 float _periodicInterval; 00110 } 00111 00112 + (id)buttonWithTitle:(CPString)aTitle 00113 { 00114 return [self buttonWithTitle:aTitle theme:[CPTheme defaultTheme]]; 00115 } 00116 00117 + (id)buttonWithTitle:(CPString)aTitle theme:(CPTheme)aTheme 00118 { 00119 var button = [[self alloc] init]; 00120 00121 [button setTheme:aTheme]; 00122 [button setTitle:aTitle]; 00123 [button sizeToFit]; 00124 00125 return button; 00126 } 00127 00128 + (CPString)defaultThemeClass 00129 { 00130 return @"button"; 00131 } 00132 00133 + (id)themeAttributes 00134 { 00135 return [CPDictionary dictionaryWithObjects:[[CPNull null], 0.0, _CGInsetMakeZero(), _CGInsetMakeZero(), [CPNull null]] 00136 forKeys:[@"image", @"image-offset", @"bezel-inset", @"content-inset", @"bezel-color"]]; 00137 } 00138 00144 - (id)initWithFrame:(CGRect)aFrame 00145 { 00146 self = [super initWithFrame:aFrame]; 00147 00148 if (self) 00149 { 00150 // Should we instead override the defaults? 00151 [self setValue:CPCenterTextAlignment forThemeAttribute:@"alignment"]; 00152 [self setValue:CPCenterVerticalTextAlignment forThemeAttribute:@"vertical-alignment"]; 00153 [self setValue:CPImageLeft forThemeAttribute:@"image-position"]; 00154 [self setValue:CPScaleNone forThemeAttribute:@"image-scaling"]; 00155 00156 [self setBezelStyle:CPRoundRectBezelStyle]; 00157 [self setBordered:YES]; 00158 00159 [self _init]; 00160 } 00161 00162 return self; 00163 } 00164 00165 - (void)_init 00166 { 00167 _controlSize = CPRegularControlSize; 00168 00169 _keyEquivalent = @""; 00170 _keyEquivalentModifierMask = 0; 00171 00172 // Continuous button defaults. 00173 _periodicInterval = 0.05; 00174 _periodicDelay = 0.5; 00175 } 00176 00177 // Setting the state 00182 - (BOOL)allowsMixedState 00183 { 00184 return _allowsMixedState; 00185 } 00186 00191 - (void)setAllowsMixedState:(BOOL)aFlag 00192 { 00193 aFlag = !!aFlag; 00194 00195 if (_allowsMixedState === aFlag) 00196 return; 00197 00198 _allowsMixedState = aFlag; 00199 00200 if (!_allowsMixedState && [self state] === CPMixedState) 00201 [self setState:CPOnState]; 00202 } 00203 00208 - (void)setObjectValue:(id)anObjectValue 00209 { 00210 if (!anObjectValue || anObjectValue === @"" || ([anObjectValue intValue] === 0)) 00211 anObjectValue = CPOffState; 00212 00213 else if (![anObjectValue isKindOfClass:[CPNumber class]]) 00214 anObjectValue = CPOnState; 00215 00216 else if (anObjectValue >= CPOnState) 00217 anObjectValue = CPOnState 00218 00219 else if (anObjectValue < CPOffState) 00220 if ([self allowsMixedState]) 00221 anObjectValue = CPMixedState; 00222 00223 else 00224 anObjectValue = CPOnState; 00225 00226 [super setObjectValue:anObjectValue]; 00227 00228 switch ([self objectValue]) 00229 { 00230 case CPMixedState: [self unsetThemeState:CPThemeStateSelected]; 00231 [self setThemeState:CPButtonStateMixed]; 00232 break; 00233 00234 case CPOnState: [self unsetThemeState:CPButtonStateMixed]; 00235 [self setThemeState:CPThemeStateSelected]; 00236 break; 00237 00238 case CPOffState: [self unsetThemeState:CPThemeStateSelected | CPButtonStateMixed]; 00239 } 00240 } 00241 00249 - (CPInteger)nextState 00250 { 00251 if ([self allowsMixedState]) 00252 { 00253 var value = [self state]; 00254 00255 return value - ((value === -1) ? -2 : 1); 00256 } 00257 00258 return 1 - [self state]; 00259 } 00260 00266 - (void)setNextState 00267 { 00268 if ([self infoForBinding:CPValueBinding]) 00269 [self setAllowsMixedState:NO]; 00270 00271 [self setState:[self nextState]]; 00272 } 00273 00279 - (void)setState:(CPInteger)aState 00280 { 00281 [self setIntValue:aState]; 00282 } 00283 00287 - (CPInteger)state 00288 { 00289 return [self intValue]; 00290 } 00291 00297 - (void)setTitle:(CPString)aTitle 00298 { 00299 if (_title === aTitle) 00300 return; 00301 00302 _title = aTitle; 00303 00304 [self setNeedsLayout]; 00305 [self setNeedsDisplay:YES]; 00306 } 00307 00313 - (CPString)title 00314 { 00315 return _title; 00316 } 00317 00318 - (void)setAlternateTitle:(CPString)aTitle 00319 { 00320 if (_alternateTitle === aTitle) 00321 return; 00322 00323 _alternateTitle = aTitle; 00324 00325 [self setNeedsLayout]; 00326 [self setNeedsDisplay:YES]; 00327 } 00328 00329 - (CPString)alternateTitle 00330 { 00331 return _alternateTitle; 00332 } 00333 00334 - (void)setImage:(CPImage)anImage 00335 { 00336 [self setValue:anImage forThemeAttribute:@"image"]; 00337 } 00338 00339 - (CPImage)image 00340 { 00341 return [self valueForThemeAttribute:@"image" inState:CPThemeStateNormal]; 00342 } 00343 00348 - (void)setAlternateImage:(CPImage)anImage 00349 { 00350 [self setValue:anImage forThemeAttribute:@"image" inState:CPThemeStateHighlighted]; 00351 } 00352 00356 - (CPImage)alternateImage 00357 { 00358 return [self valueForThemeAttribute:@"image" inState:CPThemeStateHighlighted]; 00359 } 00360 00361 - (void)setImageOffset:(float)theImageOffset 00362 { 00363 [self setValue:theImageOffset forThemeAttribute:@"image-offset"]; 00364 } 00365 00366 - (float)imageOffset 00367 { 00368 return [self valueForThemeAttribute:@"image-offset"]; 00369 } 00370 00371 - (void)setShowsStateBy:(CPInteger)aMask 00372 { 00373 if (_showsStateBy === aMask) 00374 return; 00375 00376 _showsStateBy = aMask; 00377 00378 [self setNeedsDisplay:YES]; 00379 [self setNeedsLayout]; 00380 } 00381 00382 - (CPInteger)showsStateBy 00383 { 00384 return _showsStateBy; 00385 } 00386 00387 - (void)setHighlightsBy:(CPInteger)aMask 00388 { 00389 if (_highlightsBy === aMask) 00390 return; 00391 00392 _highlightsBy = aMask; 00393 00394 if ([self hasThemeState:CPThemeStateHighlighted]) 00395 { 00396 [self setNeedsDisplay:YES]; 00397 [self setNeedsLayout]; 00398 } 00399 } 00400 00401 - (void)setButtonType:(CPButtonType)aButtonType 00402 { 00403 switch (aButtonType) 00404 { 00405 case CPMomentaryLightButton: [self setHighlightsBy:CPChangeBackgroundCellMask]; 00406 [self setShowsStateBy:CPNoCellMask]; 00407 break; 00408 00409 case CPMomentaryPushInButton: [self setHighlightsBy:CPPushInCellMask | CPChangeGrayCellMask]; 00410 [self setShowsStateBy:CPNoCellMask]; 00411 break; 00412 00413 case CPMomentaryChangeButton: [self setHighlightsBy:CPContentsCellMask]; 00414 [self setShowsStateBy:CPNoCellMask]; 00415 break; 00416 00417 case CPPushOnPushOffButton: [self setHighlightsBy:CPPushInCellMask | CPChangeGrayCellMask]; 00418 [self setShowsStateBy:CPChangeBackgroundCellMask]; 00419 break; 00420 00421 case CPOnOffButton: [self setHighlightsBy:CPChangeBackgroundCellMask]; 00422 [self setShowsStateBy:CPChangeBackgroundCellMask]; 00423 break; 00424 00425 case CPToggleButton: [self setHighlightsBy:CPPushInCellMask | CPContentsCellMask]; 00426 [self setShowsStateBy:CPContentsCellMask]; 00427 break; 00428 00429 case CPSwitchButton: [CPException raise:CPInvalidArgumentException 00430 reason:"The CPSwitchButton type is not supported in Cappuccino, use the CPCheckBox class instead."]; 00431 00432 case CPRadioButton: [CPException raise:CPInvalidArgumentException 00433 reason:"The CPRadioButton type is not supported in Cappuccino, use the CPRadio class instead."]; 00434 00435 default: [CPException raise:CPInvalidArgumentException 00436 reason:"Unknown button type."]; 00437 } 00438 00439 [self setImageDimsWhenDisabled:YES]; 00440 } 00441 00442 - (void)setImageDimsWhenDisabled:(BOOL)imageShouldDimWhenDisabled 00443 { 00444 imageShouldDimWhenDisabled = !!imageShouldDimWhenDisabled; 00445 00446 if (_imageDimsWhenDisabled === imageShouldDimWhenDisabled) 00447 return; 00448 00449 _imageDimsWhenDisabled = imageShouldDimWhenDisabled; 00450 00451 if ([self hasThemeState:CPThemeStateDisabled]) 00452 { 00453 [self setNeedsDisplay:YES]; 00454 [self setNeedsLayout]; 00455 } 00456 } 00457 00458 - (BOOL)imageDimsWhenDisabled 00459 { 00460 return _imageDimsWhenDisabled; 00461 } 00462 00463 - (void)setPeriodicDelay:(float)aDelay interval:(float)anInterval 00464 { 00465 _periodicDelay = aDelay; 00466 _periodicInterval = anInterval; 00467 } 00468 00469 - (void)mouseDown:(CPEvent)anEvent 00470 { 00471 if ([self isContinuous]) 00472 { 00473 _continuousDelayTimer = [CPTimer scheduledTimerWithTimeInterval:_periodicDelay callback: function() 00474 { 00475 if (!_continuousTimer) 00476 _continuousTimer = [CPTimer scheduledTimerWithTimeInterval:_periodicInterval target:self selector:@selector(onContinousEvent:) userInfo:anEvent repeats:YES]; 00477 } 00478 00479 repeats:NO]; 00480 } 00481 00482 [super mouseDown:anEvent]; 00483 } 00484 00485 - (void)onContinousEvent:(CPTimer)aTimer 00486 { 00487 if (_target && _action && [_target respondsToSelector:_action]) 00488 [_target performSelector:_action withObject:self]; 00489 } 00490 00491 - (BOOL)startTrackingAt:(CGPoint)aPoint 00492 { 00493 [self highlight:YES]; 00494 00495 return [super startTrackingAt:aPoint]; 00496 } 00497 00498 - (void)stopTracking:(CGPoint)lastPoint at:(CGPoint)aPoint mouseIsUp:(BOOL)mouseIsUp 00499 { 00500 [self highlight:NO]; 00501 [self invalidateTimers]; 00502 00503 [super stopTracking:lastPoint at:aPoint mouseIsUp:mouseIsUp]; 00504 00505 if (mouseIsUp && CGRectContainsPoint([self bounds], aPoint)) 00506 [self setNextState]; 00507 } 00508 00509 - (void)invalidateTimers 00510 { 00511 if (_continuousTimer) 00512 { 00513 [_continuousTimer invalidate]; 00514 _continuousTimer = nil; 00515 } 00516 00517 if (_continuousDelayTimer) 00518 { 00519 [_continuousDelayTimer invalidate]; 00520 _continuousDelayTimer = nil; 00521 } 00522 } 00523 00524 - (CGRect)contentRectForBounds:(CGRect)bounds 00525 { 00526 var contentInset = [self currentValueForThemeAttribute:@"content-inset"]; 00527 00528 if (_CGInsetIsEmpty(contentInset)) 00529 return bounds; 00530 00531 bounds = _CGRectMakeCopy(bounds); 00532 bounds.origin.x += contentInset.left; 00533 bounds.origin.y += contentInset.top; 00534 bounds.size.width -= contentInset.left + contentInset.right; 00535 bounds.size.height -= contentInset.top + contentInset.bottom; 00536 00537 return bounds; 00538 } 00539 00540 - (CGRect)bezelRectForBounds:(CGRect)bounds 00541 { 00542 if (![self isBordered]) 00543 return _CGRectMakeZero(); 00544 00545 var bezelInset = [self currentValueForThemeAttribute:@"bezel-inset"]; 00546 00547 if (_CGInsetIsEmpty(bezelInset)) 00548 return bounds; 00549 00550 bounds = _CGRectMakeCopy(bounds); 00551 bounds.origin.x += bezelInset.left; 00552 bounds.origin.y += bezelInset.top; 00553 bounds.size.width -= bezelInset.left + bezelInset.right; 00554 bounds.size.height -= bezelInset.top + bezelInset.bottom; 00555 00556 return bounds; 00557 } 00558 00559 - (CGSize)_minimumFrameSize 00560 { 00561 var size = CGSizeMakeZero(), 00562 contentView = [self ephemeralSubviewNamed:@"content-view"]; 00563 00564 if (contentView) 00565 { 00566 [contentView sizeToFit]; 00567 size = [contentView frameSize]; 00568 } 00569 else 00570 size = [([self title] || " ") sizeWithFont:[self currentValueForThemeAttribute:@"font"]]; 00571 00572 var contentInset = [self currentValueForThemeAttribute:@"content-inset"], 00573 minSize = [self currentValueForThemeAttribute:@"min-size"], 00574 maxSize = [self currentValueForThemeAttribute:@"max-size"]; 00575 00576 size.width = MAX(size.width + contentInset.left + contentInset.right, minSize.width); 00577 size.height = MAX(size.height + contentInset.top + contentInset.bottom, minSize.height); 00578 00579 if (maxSize.width >= 0.0) 00580 size.width = MIN(size.width, maxSize.width); 00581 00582 if (maxSize.height >= 0.0) 00583 size.height = MIN(size.height, maxSize.height); 00584 00585 return size; 00586 } 00587 00591 - (void)sizeToFit 00592 { 00593 [self layoutSubviews]; 00594 00595 [self setFrameSize:[self _minimumFrameSize]]; 00596 00597 if ([self ephemeralSubviewNamed:@"content-view"]) 00598 [self layoutSubviews]; 00599 } 00600 00601 - (CGRect)rectForEphemeralSubviewNamed:(CPString)aName 00602 { 00603 if (aName === "bezel-view") 00604 return [self bezelRectForBounds:[self bounds]]; 00605 00606 else if (aName === "content-view") 00607 return [self contentRectForBounds:[self bounds]]; 00608 00609 return [super rectForEphemeralSubviewNamed:aName]; 00610 } 00611 00612 - (CPView)createEphemeralSubviewNamed:(CPString)aName 00613 { 00614 if (aName === "bezel-view") 00615 { 00616 var view = [[CPView alloc] initWithFrame:_CGRectMakeZero()]; 00617 00618 [view setHitTests:NO]; 00619 00620 return view; 00621 } 00622 else 00623 return [[_CPImageAndTextView alloc] initWithFrame:_CGRectMakeZero()]; 00624 } 00625 00626 - (void)layoutSubviews 00627 { 00628 var bezelView = [self layoutEphemeralSubviewNamed:@"bezel-view" 00629 positioned:CPWindowBelow 00630 relativeToEphemeralSubviewNamed:@"content-view"]; 00631 00632 [bezelView setBackgroundColor:[self currentValueForThemeAttribute:@"bezel-color"]]; 00633 00634 var contentView = [self layoutEphemeralSubviewNamed:@"content-view" 00635 positioned:CPWindowAbove 00636 relativeToEphemeralSubviewNamed:@"bezel-view"]; 00637 00638 if (contentView) 00639 { 00640 [contentView setText:([self hasThemeState:CPThemeStateHighlighted] && _alternateTitle) ? _alternateTitle : _title]; 00641 [contentView setImage:[self currentValueForThemeAttribute:@"image"]]; 00642 [contentView setImageOffset:[self currentValueForThemeAttribute:@"image-offset"]]; 00643 00644 [contentView setFont:[self currentValueForThemeAttribute:@"font"]]; 00645 [contentView setTextColor:[self currentValueForThemeAttribute:@"text-color"]]; 00646 [contentView setAlignment:[self currentValueForThemeAttribute:@"alignment"]]; 00647 [contentView setVerticalAlignment:[self currentValueForThemeAttribute:@"vertical-alignment"]]; 00648 [contentView setLineBreakMode:[self currentValueForThemeAttribute:@"line-break-mode"]]; 00649 [contentView setTextShadowColor:[self currentValueForThemeAttribute:@"text-shadow-color"]]; 00650 [contentView setTextShadowOffset:[self currentValueForThemeAttribute:@"text-shadow-offset"]]; 00651 [contentView setImagePosition:[self currentValueForThemeAttribute:@"image-position"]]; 00652 [contentView setImageScaling:[self currentValueForThemeAttribute:@"image-scaling"]]; 00653 [contentView setDimsImage:[self hasThemeState:CPThemeStateDisabled] && _imageDimsWhenDisabled]; 00654 } 00655 } 00656 00657 - (void)setBordered:(BOOL)shouldBeBordered 00658 { 00659 if (shouldBeBordered) 00660 [self setThemeState:CPThemeStateBordered]; 00661 else 00662 [self unsetThemeState:CPThemeStateBordered]; 00663 } 00664 00665 - (BOOL)isBordered 00666 { 00667 return [self hasThemeState:CPThemeStateBordered]; 00668 } 00669 00676 - (void)setKeyEquivalent:(CPString)aString 00677 { 00678 _keyEquivalent = aString || @""; 00679 00680 // Check if the key equivalent is the enter key 00681 // Treat \r and \n as the same key equivalent. See issue #710. 00682 if (aString === CPNewlineCharacter || aString === CPCarriageReturnCharacter) 00683 [self setThemeState:CPThemeStateDefault]; 00684 else 00685 [self unsetThemeState:CPThemeStateDefault]; 00686 } 00687 00688 - (void)viewWillMoveToWindow:(CPWindow)aWindow 00689 { 00690 var selfWindow = [self window]; 00691 00692 if (selfWindow === aWindow || aWindow === nil) 00693 return; 00694 00695 if ([selfWindow defaultButton] === self) 00696 [selfWindow setDefaultButton:nil]; 00697 00698 if ([self keyEquivalent] === CPNewlineCharacter || [self keyEquivalent] === CPCarriageReturnCharacter) 00699 [aWindow setDefaultButton:self]; 00700 } 00701 00705 - (CPString)keyEquivalent 00706 { 00707 return _keyEquivalent; 00708 } 00709 00713 - (void)setKeyEquivalentModifierMask:(unsigned)aMask 00714 { 00715 _keyEquivalentModifierMask = aMask; 00716 } 00717 00721 - (unsigned)keyEquivalentModifierMask 00722 { 00723 return _keyEquivalentModifierMask; 00724 } 00725 00730 - (BOOL)performKeyEquivalent:(CPEvent)anEvent 00731 { 00732 // Don't handle the key equivalent for the default window because the window will handle it for us 00733 if ([[self window] defaultButton] === self) 00734 return NO; 00735 00736 if (![anEvent _triggersKeyEquivalent:[self keyEquivalent] withModifierMask:[self keyEquivalentModifierMask]]) 00737 return NO; 00738 00739 [self performClick:nil]; 00740 return YES; 00741 } 00742 00743 @end 00744 00745 @implementation CPButton (NS) 00746 00747 - (void)setBezelStyle:(unsigned)aBezelStyle 00748 { 00749 if (aBezelStyle === _bezelStyle) 00750 return; 00751 00752 var currentState = [CPButtonBezelStyleStateMap objectForKey:_bezelStyle], 00753 newState = [CPButtonBezelStyleStateMap objectForKey:aBezelStyle]; 00754 00755 if (currentState) 00756 [self unsetThemeState:currentState]; 00757 00758 if (newState) 00759 [self setThemeState:newState]; 00760 00761 _bezelStyle = aBezelStyle; 00762 } 00763 00764 - (unsigned)bezelStyle 00765 { 00766 return _bezelStyle; 00767 } 00768 00769 @end 00770 00771 00772 var CPButtonImageKey = @"CPButtonImageKey", 00773 CPButtonAlternateImageKey = @"CPButtonAlternateImageKey", 00774 CPButtonTitleKey = @"CPButtonTitleKey", 00775 CPButtonAlternateTitleKey = @"CPButtonAlternateTitleKey", 00776 CPButtonIsBorderedKey = @"CPButtonIsBorderedKey", 00777 CPButtonAllowsMixedStateKey = @"CPButtonAllowsMixedStateKey", 00778 CPButtonImageDimsWhenDisabledKey = @"CPButtonImageDimsWhenDisabledKey", 00779 CPButtonImagePositionKey = @"CPButtonImagePositionKey", 00780 CPButtonKeyEquivalentKey = @"CPButtonKeyEquivalentKey", 00781 CPButtonKeyEquivalentMaskKey = @"CPButtonKeyEquivalentMaskKey", 00782 CPButtonPeriodicDelayKey = @"CPButtonPeriodicDelayKey", 00783 CPButtonPeriodicIntervalKey = @"CPButtonPeriodicIntervalKey"; 00784 00785 @implementation CPButton (CPCoding) 00786 00791 - (id)initWithCoder:(CPCoder)aCoder 00792 { 00793 self = [super initWithCoder:aCoder]; 00794 00795 if (self) 00796 { 00797 [self _init]; 00798 00799 _title = [aCoder decodeObjectForKey:CPButtonTitleKey]; 00800 _alternateTitle = [aCoder decodeObjectForKey:CPButtonAlternateTitleKey]; 00801 00802 if ([aCoder containsValueForKey:CPButtonAllowsMixedStateKey]) 00803 _allowsMixedState = [aCoder decodeBoolForKey:CPButtonAllowsMixedStateKey]; 00804 00805 [self setImageDimsWhenDisabled:[aCoder decodeObjectForKey:CPButtonImageDimsWhenDisabledKey]]; 00806 00807 if ([aCoder containsValueForKey:CPButtonImagePositionKey]) 00808 [self setImagePosition:[aCoder decodeIntForKey:CPButtonImagePositionKey]]; 00809 00810 if ([aCoder containsValueForKey:CPButtonKeyEquivalentKey]) 00811 [self setKeyEquivalent:CFData.decodeBase64ToUtf16String([aCoder decodeObjectForKey:CPButtonKeyEquivalentKey])]; 00812 00813 if ([aCoder containsValueForKey:CPButtonPeriodicDelayKey]) 00814 _periodicDelay = [aCoder decodeObjectForKey:CPButtonPeriodicDelayKey]; 00815 00816 if ([aCoder containsValueForKey:CPButtonPeriodicIntervalKey]) 00817 _periodicInterval = [aCoder decodeObjectForKey:CPButtonPeriodicIntervalKey]; 00818 00819 _keyEquivalentModifierMask = [aCoder decodeIntForKey:CPButtonKeyEquivalentMaskKey]; 00820 00821 [self setNeedsLayout]; 00822 [self setNeedsDisplay:YES]; 00823 } 00824 00825 return self; 00826 } 00827 00832 - (void)encodeWithCoder:(CPCoder)aCoder 00833 { 00834 [super encodeWithCoder:aCoder]; 00835 [self invalidateTimers]; 00836 00837 [aCoder encodeObject:_title forKey:CPButtonTitleKey]; 00838 [aCoder encodeObject:_alternateTitle forKey:CPButtonAlternateTitleKey]; 00839 00840 [aCoder encodeBool:_allowsMixedState forKey:CPButtonAllowsMixedStateKey]; 00841 00842 [aCoder encodeBool:[self imageDimsWhenDisabled] forKey:CPButtonImageDimsWhenDisabledKey]; 00843 [aCoder encodeInt:[self imagePosition] forKey:CPButtonImagePositionKey]; 00844 00845 if (_keyEquivalent) 00846 [aCoder encodeObject:CFData.encodeBase64Utf16String(_keyEquivalent) forKey:CPButtonKeyEquivalentKey]; 00847 00848 [aCoder encodeInt:_keyEquivalentModifierMask forKey:CPButtonKeyEquivalentMaskKey]; 00849 00850 [aCoder encodeObject:_periodicDelay forKey:CPButtonPeriodicDelayKey]; 00851 [aCoder encodeObject:_periodicInterval forKey:CPButtonPeriodicIntervalKey]; 00852 } 00853 00854 @end 00855