00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 @import <Foundation/CPCoder.j>
00024 @import <Foundation/CPObject.j>
00025 @import <Foundation/CPString.j>
00026
00027 @import "CPImage.j"
00028 @import "CPMenu.j"
00029 @import "CPView.j"
00030 @import "_CPMenuItemView.j"
00031
00040 @implementation CPMenuItem : CPObject
00041 {
00042 BOOL _isSeparator;
00043
00044 CPString _title;
00045
00046
00047 CPFont _font;
00048
00049 id _target;
00050 SEL _action;
00051
00052 BOOL _isEnabled;
00053 BOOL _isHidden;
00054
00055 int _tag;
00056 int _state;
00057
00058 CPImage _image;
00059 CPImage _alternateImage;
00060 CPImage _onStateImage;
00061 CPImage _offStateImage;
00062 CPImage _mixedStateImage;
00063
00064 CPMenu _submenu;
00065 CPMenu _menu;
00066
00067 CPString _keyEquivalent;
00068 unsigned _keyEquivalentModifierMask;
00069
00070 int _mnemonicLocation;
00071
00072 BOOL _isAlternate;
00073 int _indentationLevel;
00074
00075 CPString _toolTip;
00076 id _representedObject;
00077 CPView _view;
00078
00079 _CPMenuItemView _menuItemView;
00080 }
00081
00082 - (id)init
00083 {
00084 return [self initWithTitle:@"" action:nil keyEquivalent:nil];
00085 }
00086
00094 - (id)initWithTitle:(CPString)aTitle action:(SEL)anAction keyEquivalent:(CPString)aKeyEquivalent
00095 {
00096 self = [super init];
00097
00098 if (self)
00099 {
00100 _isSeparator = NO;
00101
00102 _title = aTitle;
00103 _action = anAction;
00104
00105 _isEnabled = YES;
00106
00107 _tag = 0;
00108 _state = CPOffState;
00109
00110 _keyEquivalent = aKeyEquivalent || @"";
00111 _keyEquivalentModifierMask = CPPlatformActionKeyMask;
00112
00113 _indentationLevel = 0;
00114
00115 _mnemonicLocation = CPNotFound;
00116 }
00117
00118 return self;
00119 }
00120
00121
00126 - (void)setEnabled:(BOOL)isEnabled
00127 {
00128 if ([_menu autoenablesItems])
00129 return;
00130
00131 _isEnabled = isEnabled;
00132
00133 [_menuItemView setDirty];
00134
00135 [_menu itemChanged:self];
00136 }
00137
00141 - (BOOL)isEnabled
00142 {
00143 return _isEnabled;
00144 }
00145
00146
00151 - (void)setHidden:(BOOL)isHidden
00152 {
00153 if (_isHidden == isHidden)
00154 return;
00155
00156 _isHidden = isHidden;
00157
00158 [_menu itemChanged:self];
00159 }
00160
00164 - (BOOL)isHidden
00165 {
00166 return _isHidden;
00167 }
00168
00172 - (BOOL)isHiddenOrHasHiddenAncestor
00173 {
00174 if (_isHidden)
00175 return YES;
00176
00177 var supermenu = [_menu supermenu];
00178
00179 if ([[supermenu itemAtIndex:[supermenu indexOfItemWithSubmenu:_menu]] isHiddenOrHasHiddenAncestor])
00180 return YES;
00181
00182 return NO;
00183 }
00184
00185
00190 - (void)setTarget:(id)aTarget
00191 {
00192 _target = aTarget;
00193 }
00194
00198 - (id)target
00199 {
00200 return _target;
00201 }
00202
00207 - (void)setAction:(SEL)anAction
00208 {
00209 _action = anAction;
00210 }
00211
00215 - (SEL)action
00216 {
00217 return _action;
00218 }
00219
00220
00225 - (void)setTitle:(CPString)aTitle
00226 {
00227 _mnemonicLocation = CPNotFound;
00228
00229 if (_title == aTitle)
00230 return;
00231
00232 _title = aTitle;
00233
00234 [_menuItemView setDirty];
00235
00236 [_menu itemChanged:self];
00237 }
00238
00242 - (CPString)title
00243 {
00244 return _title;
00245 }
00246
00250 - (void)setTextColor:(CPString)aColor
00251 {
00252
00253 }
00254
00259 - (void)setFont:(CPFont)aFont
00260 {
00261 if (_font == aFont)
00262 return;
00263
00264 _font = aFont;
00265
00266 [_menu itemChanged:self];
00267
00268 [_menuItemView setDirty];
00269 }
00270
00274 - (CPFont)font
00275 {
00276 return _font;
00277 }
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00294 - (void)setTag:(int)aTag
00295 {
00296 _tag = aTag;
00297 }
00298
00302 - (int)tag
00303 {
00304 return _tag;
00305 }
00306
00315 - (void)setState:(int)aState
00316 {
00317 if (_state == aState)
00318 return;
00319
00320 _state = aState;
00321
00322 [_menu itemChanged:self];
00323
00324 [_menuItemView setDirty];
00325 }
00326
00335 - (int)state
00336 {
00337 return _state;
00338 }
00339
00340
00345 - (void)setImage:(CPImage)anImage
00346 {
00347 if (_image == anImage)
00348 return;
00349
00350 _image = anImage;
00351
00352 [_menuItemView setDirty];
00353
00354 [_menu itemChanged:self];
00355 }
00356
00360 - (CPImage)image
00361 {
00362 return _image;
00363 }
00364
00369 - (void)setAlternateImage:(CPImage)anImage
00370 {
00371 _alternateImage = anImage;
00372 }
00373
00377 - (CPImage)alternateImage
00378 {
00379 return _alternateImage;
00380 }
00381
00387 - (void)setOnStateImage:(CPImage)anImage
00388 {
00389 if (_onStateImage == anImage)
00390 return;
00391
00392 _onStateImage = anImage;
00393 [_menu itemChanged:self];
00394 }
00395
00399 - (CPImage)onStateImage
00400 {
00401 return _onStateImage;
00402 }
00403
00408 - (void)setOffStateImage:(CPImage)anImage
00409 {
00410 if (_offStateImage == anImage)
00411 return;
00412
00413 _offStateImage = anImage;
00414 [_menu itemChanged:self];
00415 }
00416
00420 - (CPImage)offStateImage
00421 {
00422 return _offStateImage;
00423 }
00424
00429 - (void)setMixedStateImage:(CPImage)anImage
00430 {
00431 if (_mixedStateImage == anImage)
00432 return;
00433
00434 _mixedStateImage = anImage;
00435 [_menu itemChanged:self];
00436 }
00437
00442 - (CPImage)mixedStateImage
00443 {
00444 return _mixedStateImage;
00445 }
00446
00447
00452 - (void)setSubmenu:(CPMenu)aMenu
00453 {
00454 if (_submenu === aMenu)
00455 return;
00456
00457 var supermenu = [_submenu supermenu];
00458
00459 if (supermenu)
00460 [CPException raise:CPInvalidArgumentException
00461 reason: @"Can't add submenu \"" + [aMenu title] + "\" to item \"" + [self title] + "\", because it is already submenu of \"" + [[aMenu supermenu] title] + "\""];
00462
00463 _submenu = aMenu;
00464
00465 if (_submenu)
00466 {
00467 [_submenu setSupermenu:_menu];
00468
00469 [self setTarget:_menu];
00470 [self setAction:@selector(submenuAction:)];
00471 }
00472 else
00473 {
00474 [self setTarget:nil];
00475 [self setAction:NULL];
00476 }
00477
00478 [_menuItemView setDirty];
00479
00480 [_menu itemChanged:self];
00481 }
00482
00486 - (CPMenu)submenu
00487 {
00488 return _submenu;
00489 }
00490
00494 - (BOOL)hasSubmenu
00495 {
00496 return _submenu ? YES : NO;
00497 }
00498
00499
00500
00504 + (CPMenuItem)separatorItem
00505 {
00506 var separatorItem = [[self alloc] initWithTitle:@"" action:nil keyEquivalent:nil];
00507
00508 separatorItem._isSeparator = YES;
00509
00510 return separatorItem;
00511 }
00512
00516 - (BOOL)isSeparatorItem
00517 {
00518 return _isSeparator;
00519 }
00520
00521
00526 - (void)setMenu:(CPMenu)aMenu
00527 {
00528 _menu = aMenu;
00529 }
00530
00534 - (CPMenu)menu
00535 {
00536 return _menu;
00537 }
00538
00539
00540
00545 - (void)setKeyEquivalent:(CPString)aString
00546 {
00547 _keyEquivalent = aString || @"";
00548 }
00549
00553 - (CPString)keyEquivalent
00554 {
00555 return _keyEquivalent;
00556 }
00557
00568 - (void)setKeyEquivalentModifierMask:(unsigned)aMask
00569 {
00570 _keyEquivalentModifierMask = aMask;
00571 }
00572
00583 - (unsigned)keyEquivalentModifierMask
00584 {
00585 return _keyEquivalentModifierMask;
00586 }
00587
00588 - (CPString)keyEquivalentStringRepresentation
00589 {
00590 if (![_keyEquivalent length])
00591 return @"";
00592
00593 var string = _keyEquivalent.toUpperCase(),
00594 needsShift = _keyEquivalentModifierMask & CPShiftKeyMask || string === _keyEquivalent;
00595
00596 if (CPBrowserIsOperatingSystem(CPMacOperatingSystem))
00597 {
00598 if (_keyEquivalentModifierMask & CPCommandKeyMask)
00599 string = "⌘" + string;
00600
00601 if (needsShift)
00602 string = "⇧" + string;
00603
00604 if (_keyEquivalentModifierMask & CPAlternateKeyMask)
00605 string = "⌥" + string;
00606
00607 if (_keyEquivalentModifierMask & CPControlKeyMask)
00608 string = "^" + string;
00609 }
00610 else
00611 {
00612 if (needsShift)
00613 string = "Shift-" + string;
00614
00615 if (_keyEquivalentModifierMask & CPAlternateKeyMask)
00616 string = "Alt-" + string;
00617
00618 if (_keyEquivalentModifierMask & CPControlKeyMask || _keyEquivalentModifierMask & CPCommandKeyMask)
00619 string = "Ctrl-" + string;
00620 }
00621
00622 return string;
00623 }
00624
00625
00631 - (void)setMnemonicLocation:(unsigned)aLocation
00632 {
00633 _mnemonicLocation = aLocation;
00634 }
00635
00639 - (unsigned)mnemonicLocation
00640 {
00641 return _mnemonicLocation;
00642 }
00643
00648 - (void)setTitleWithMnemonicLocation:(CPString)aTitle
00649 {
00650 var location = [aTitle rangeOfString:@"&"].location;
00651
00652 if (location == CPNotFound)
00653 [self setTitle:aTitle];
00654 else
00655 {
00656 [self setTitle:[aTitle substringToIndex:location] + [aTitle substringFromIndex:location + 1]];
00657 [self setMnemonicLocation:location];
00658 }
00659 }
00660
00664 - (CPString)mnemonic
00665 {
00666 return _mnemonicLocation == CPNotFound ? @"" : [_title characterAtIndex:_mnemonicLocation];
00667 }
00668
00669
00670
00675 - (void)setAlternate:(BOOL)isAlternate
00676 {
00677 _isAlternate = isAlternate;
00678 }
00679
00683 - (BOOL)isAlternate
00684 {
00685 return _isAlternate;
00686 }
00687
00688
00689
00695 - (void)setIndentationLevel:(unsigned)aLevel
00696 {
00697 if (aLevel < 0)
00698 [CPException raise:CPInvalidArgumentException reason:"setIndentationLevel: argument must be greater than or equal to 0."];
00699
00700 _indentationLevel = MIN(15, aLevel);
00701 }
00702
00706 - (unsigned)indentationLevel
00707 {
00708 return _indentationLevel;
00709 }
00710
00711
00716 - (void)setToolTip:(CPString)aToolTip
00717 {
00718 _toolTip = aToolTip;
00719 }
00720
00724 - (CPString)toolTip
00725 {
00726 return _toolTip;
00727 }
00728
00729
00730
00735 - (void)setRepresentedObject:(id)anObject
00736 {
00737 _representedObject = anObject;
00738 }
00739
00743 - (id)representedObject
00744 {
00745 return _representedObject;
00746 }
00747
00748
00749
00754 - (void)setView:(CPView)aView
00755 {
00756 if (_view === aView)
00757 return;
00758
00759 _view = aView;
00760
00761 [_menuItemView setDirty];
00762
00763 [_menu itemChanged:self];
00764 }
00765
00769 - (CPView)view
00770 {
00771 return _view;
00772 }
00773
00774
00775
00779 - (BOOL)isHighlighted
00780 {
00781 return [[self menu] highlightedItem] == self;
00782 }
00783
00784
00785
00786
00787
00788
00789 - (id)_menuItemView
00790 {
00791 if (!_menuItemView)
00792 _menuItemView = [[_CPMenuItemView alloc] initWithFrame:CGRectMakeZero() forMenuItem:self];
00793
00794 return _menuItemView;
00795 }
00796
00797 - (BOOL)_isSelectable
00798 {
00799 return ![self submenu] || [self action] !== @selector(submenuAction:) || [self target] !== [self menu];
00800 }
00801
00802 - (BOOL)_isMenuBarButton
00803 {
00804 return ![self submenu] && [self menu] === [CPApp mainMenu];
00805 }
00806
00807 @end
00808
00809 var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey",
00810
00811 CPMenuItemTitleKey = @"CPMenuItemTitleKey",
00812 CPMenuItemTargetKey = @"CPMenuItemTargetKey",
00813 CPMenuItemActionKey = @"CPMenuItemActionKey",
00814
00815 CPMenuItemIsEnabledKey = @"CPMenuItemIsEnabledKey",
00816 CPMenuItemIsHiddenKey = @"CPMenuItemIsHiddenKey",
00817
00818 CPMenuItemTagKey = @"CPMenuItemTagKey",
00819 CPMenuItemStateKey = @"CPMenuItemStateKey",
00820
00821 CPMenuItemImageKey = @"CPMenuItemImageKey",
00822 CPMenuItemAlternateImageKey = @"CPMenuItemAlternateImageKey",
00823
00824 CPMenuItemSubmenuKey = @"CPMenuItemSubmenuKey",
00825 CPMenuItemMenuKey = @"CPMenuItemMenuKey",
00826
00827 CPMenuItemKeyEquivalentKey = @"CPMenuItemKeyEquivalentKey",
00828 CPMenuItemKeyEquivalentModifierMaskKey = @"CPMenuItemKeyEquivalentModifierMaskKey",
00829
00830 CPMenuItemIndentationLevelKey = @"CPMenuItemIndentationLevelKey",
00831
00832 CPMenuItemRepresentedObjectKey = @"CPMenuItemRepresentedObjectKey",
00833 CPMenuItemViewKey = @"CPMenuItemViewKey";
00834
00835 #define DEFAULT_VALUE(aKey, aDefaultValue) [aCoder containsValueForKey:(aKey)] ? [aCoder decodeObjectForKey:(aKey)] : (aDefaultValue)
00836 #define ENCODE_IFNOT(aKey, aValue, aDefaultValue) if ((aValue) !== (aDefaultValue)) [aCoder encodeObject:(aValue) forKey:(aKey)];
00837
00838 @implementation CPMenuItem (CPCoding)
00844 - (id)initWithCoder:(CPCoder)aCoder
00845 {
00846 self = [super init];
00847
00848 if (self)
00849 {
00850 _isSeparator = [aCoder containsValueForKey:CPMenuItemIsSeparatorKey] && [aCoder decodeBoolForKey:CPMenuItemIsSeparatorKey];
00851
00852 _title = [aCoder decodeObjectForKey:CPMenuItemTitleKey];
00853
00854
00855
00856 _target = [aCoder decodeObjectForKey:CPMenuItemTargetKey];
00857 _action = [aCoder decodeObjectForKey:CPMenuItemActionKey];
00858
00859 _isEnabled = DEFAULT_VALUE(CPMenuItemIsEnabledKey, YES);
00860 _isHidden = DEFAULT_VALUE(CPMenuItemIsHiddenKey, NO);
00861 _tag = DEFAULT_VALUE(CPMenuItemTagKey, 0);
00862 _state = DEFAULT_VALUE(CPMenuItemStateKey, CPOffState);
00863
00864
00865 _image = DEFAULT_VALUE(CPMenuItemImageKey, nil);
00866 _alternateImage = DEFAULT_VALUE(CPMenuItemAlternateImageKey, nil);
00867
00868
00869
00870
00871
00872 _menu = DEFAULT_VALUE(CPMenuItemMenuKey, nil);
00873 [self setSubmenu:DEFAULT_VALUE(CPMenuItemSubmenuKey, nil)];
00874
00875 _keyEquivalent = [aCoder decodeObjectForKey:CPMenuItemKeyEquivalentKey] || @"";
00876 _keyEquivalentModifierMask = [aCoder decodeObjectForKey:CPMenuItemKeyEquivalentModifierMaskKey] || 0;
00877
00878
00879
00880
00881
00882
00883 [self setIndentationLevel:[aCoder decodeIntForKey:CPMenuItemIndentationLevelKey] || 0];
00884
00885
00886
00887 _representedObject = DEFAULT_VALUE(CPMenuItemRepresentedObjectKey, nil);
00888 _view = DEFAULT_VALUE(CPMenuItemViewKey, nil);
00889 }
00890
00891 return self;
00892 }
00893
00898 - (void)encodeWithCoder:(CPCoder)aCoder
00899 {
00900 if (_isSeparator)
00901 [aCoder encodeBool:_isSeparator forKey:CPMenuItemIsSeparatorKey];
00902
00903 [aCoder encodeObject:_title forKey:CPMenuItemTitleKey];
00904
00905 [aCoder encodeObject:_target forKey:CPMenuItemTargetKey];
00906 [aCoder encodeObject:_action forKey:CPMenuItemActionKey];
00907
00908 ENCODE_IFNOT(CPMenuItemIsEnabledKey, _isEnabled, YES);
00909 ENCODE_IFNOT(CPMenuItemIsHiddenKey, _isHidden, NO);
00910
00911 ENCODE_IFNOT(CPMenuItemTagKey, _tag, 0);
00912 ENCODE_IFNOT(CPMenuItemStateKey, _state, CPOffState);
00913
00914 ENCODE_IFNOT(CPMenuItemImageKey, _image, nil);
00915 ENCODE_IFNOT(CPMenuItemAlternateImageKey, _alternateImage, nil);
00916
00917 ENCODE_IFNOT(CPMenuItemSubmenuKey, _submenu, nil);
00918 ENCODE_IFNOT(CPMenuItemMenuKey, _menu, nil);
00919
00920 if (_keyEquivalent && _keyEquivalent.length)
00921 [aCoder encodeObject:_keyEquivalent forKey:CPMenuItemKeyEquivalentKey];
00922
00923 if (_keyEquivalentModifierMask)
00924 [aCoder encodeObject:_keyEquivalentModifierMask forKey:CPMenuItemKeyEquivalentModifierMaskKey];
00925
00926 if (_indentationLevel > 0)
00927 [aCoder encodeInt:_indentationLevel forKey:CPMenuItemIndentationLevelKey];
00928
00929 ENCODE_IFNOT(CPMenuItemRepresentedObjectKey, _representedObject, nil);
00930 ENCODE_IFNOT(CPMenuItemViewKey, _view, nil);
00931 }
00932
00933 @end