mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-09-16 15:41:27 +00:00
1038 lines
23 KiB
Plaintext
1038 lines
23 KiB
Plaintext
/*
|
|
* CPMenuItem.j
|
|
* AppKit
|
|
*
|
|
* Created by Francisco Tolmasky.
|
|
* Copyright 2008, 280 North, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
@import <Foundation/CPCoder.j>
|
|
@import <Foundation/CPObject.j>
|
|
@import <Foundation/CPString.j>
|
|
|
|
@import "CPImage.j"
|
|
@import "CPText.j"
|
|
@import "CPView.j"
|
|
@import "_CPMenuItemView.j"
|
|
|
|
@class CPMenu
|
|
|
|
@global CPApp
|
|
|
|
var CPMenuItemStringRepresentationDictionary = @{
|
|
CPEscapeFunctionKey: "\u238B",
|
|
CPTabCharacter: "\u21E5",
|
|
CPBackTabCharacter: "\u21E4",
|
|
CPSpaceFunctionKey: "\u2423",
|
|
CPCarriageReturnCharacter: "\u23CE",
|
|
CPBackspaceCharacter: "\u232B",
|
|
CPDeleteFunctionKey: "\u232B",
|
|
CPDeleteCharacter: "\u2326",
|
|
CPHomeFunctionKey: "\u21F1",
|
|
CPEndFunctionKey: "\u21F2",
|
|
CPPageUpFunctionKey: "\u21DE",
|
|
CPPageDownFunctionKey: "\u21DF",
|
|
CPUpArrowFunctionKey: "\u2191",
|
|
CPDownArrowFunctionKey: "\u2193",
|
|
CPLeftArrowFunctionKey: "\u2190",
|
|
CPRightArrowFunctionKey: "\u2192",
|
|
CPClearDisplayFunctionKey: "\u2327",
|
|
};
|
|
|
|
/*!
|
|
@ingroup appkit
|
|
@class CPMenuItem
|
|
|
|
A CPMenuItem is added to a CPMenu.
|
|
It has an action and a target for that action to be sent to
|
|
whenever the item is 'activated'.
|
|
*/
|
|
@implementation CPMenuItem : CPObject
|
|
{
|
|
BOOL _isSeparator;
|
|
|
|
CPString _title;
|
|
//CPAttributedString _attributedTitle;
|
|
|
|
CPFont _font;
|
|
|
|
id _target;
|
|
SEL _action;
|
|
|
|
BOOL _isEnabled;
|
|
BOOL _isHidden;
|
|
|
|
int _tag;
|
|
int _state;
|
|
|
|
CPImage _image;
|
|
CPImage _alternateImage;
|
|
CPImage _onStateImage;
|
|
CPImage _offStateImage;
|
|
CPImage _mixedStateImage;
|
|
|
|
CPMenu _submenu;
|
|
CPMenu _menu;
|
|
|
|
CPString _keyEquivalent;
|
|
unsigned _keyEquivalentModifierMask;
|
|
|
|
int _mnemonicLocation;
|
|
|
|
BOOL _isAlternate;
|
|
int _indentationLevel;
|
|
|
|
CPString _toolTip;
|
|
id _representedObject;
|
|
CPView _view;
|
|
|
|
int _changeCount;
|
|
|
|
_CPMenuItemView _menuItemView;
|
|
}
|
|
|
|
+ (Class)_binderClassForBinding:(CPString)aBinding
|
|
{
|
|
if ([aBinding hasPrefix:CPEnabledBinding])
|
|
return [CPMultipleValueAndBinding class];
|
|
else if (aBinding === CPTargetBinding || [aBinding hasPrefix:CPArgumentBinding])
|
|
return [CPActionBinding class];
|
|
|
|
return [super _binderClassForBinding:aBinding];
|
|
}
|
|
|
|
- (id)init
|
|
{
|
|
return [self initWithTitle:@"" action:nil keyEquivalent:nil];
|
|
}
|
|
|
|
/*!
|
|
Initializes the menu item with a title, action, and keyboard equivalent.
|
|
@param aTitle the menu item's title
|
|
@param anAction the action that gets triggered when the item is selected
|
|
@param aKeyEquivalent the keyboard shortcut for the item
|
|
@return the initialized menu item
|
|
*/
|
|
- (id)initWithTitle:(CPString)aTitle action:(SEL)anAction keyEquivalent:(CPString)aKeyEquivalent
|
|
{
|
|
self = [super init];
|
|
|
|
if (self)
|
|
{
|
|
_changeCount = 0;
|
|
_isSeparator = NO;
|
|
|
|
_title = aTitle;
|
|
_action = anAction;
|
|
|
|
_isEnabled = YES;
|
|
_isHidden = NO;
|
|
|
|
_tag = 0;
|
|
_state = CPOffState;
|
|
|
|
_keyEquivalent = aKeyEquivalent || @"";
|
|
_keyEquivalentModifierMask = CPPlatformActionKeyMask;
|
|
|
|
_indentationLevel = 0;
|
|
|
|
_mnemonicLocation = CPNotFound;
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
// Enabling a Menu Item
|
|
/*!
|
|
Sets whether the menu item is enabled or not
|
|
@param isEnabled \c YES enables the item. \c NO disables it.
|
|
*/
|
|
- (void)setEnabled:(BOOL)isEnabled
|
|
{
|
|
if (_isEnabled === isEnabled)
|
|
return;
|
|
|
|
if (!isEnabled && [self isHighlighted])
|
|
[_menu _highlightItemAtIndex:CPNotFound];
|
|
|
|
_isEnabled = !!isEnabled;
|
|
|
|
[_menuItemView setDirty];
|
|
[_menu itemChanged:self];
|
|
}
|
|
|
|
/*!
|
|
Returns \c YES if the item is enabled.
|
|
*/
|
|
- (BOOL)isEnabled
|
|
{
|
|
return _isEnabled;
|
|
}
|
|
|
|
// Managing Hidden Status
|
|
/*!
|
|
Sets whether the item should be hidden. A hidden item can not be triggered by keyboard shortcuts.
|
|
@param isHidden \c YES hides the item. \c NO reveals it.
|
|
*/
|
|
- (void)setHidden:(BOOL)isHidden
|
|
{
|
|
if (_isHidden == isHidden)
|
|
return;
|
|
|
|
_isHidden = isHidden;
|
|
|
|
[_menu itemChanged:self];
|
|
}
|
|
|
|
/*!
|
|
Returns \c YES if the item is hidden.
|
|
*/
|
|
- (BOOL)isHidden
|
|
{
|
|
return _isHidden;
|
|
}
|
|
|
|
/*!
|
|
Returns \c YES if the item is hidden or if one of it's supermenus is hidden.
|
|
*/
|
|
- (BOOL)isHiddenOrHasHiddenAncestor
|
|
{
|
|
if (_isHidden)
|
|
return YES;
|
|
|
|
var supermenu = [_menu supermenu];
|
|
|
|
if ([[supermenu itemAtIndex:[supermenu indexOfItemWithSubmenu:_menu]] isHiddenOrHasHiddenAncestor])
|
|
return YES;
|
|
|
|
return NO;
|
|
}
|
|
|
|
// Managing Target and Action
|
|
/*!
|
|
Sets the menu item's action target.
|
|
@param aTarget the target for the action
|
|
*/
|
|
- (void)setTarget:(id)aTarget
|
|
{
|
|
_target = aTarget;
|
|
}
|
|
|
|
/*!
|
|
Returns the item's action target
|
|
*/
|
|
- (id)target
|
|
{
|
|
return _target;
|
|
}
|
|
|
|
/*!
|
|
Sets the action that gets sent to the item's target when triggered.
|
|
@param anAction the action to send
|
|
*/
|
|
- (void)setAction:(SEL)anAction
|
|
{
|
|
_action = anAction;
|
|
}
|
|
|
|
/*!
|
|
Returns the item's action.
|
|
*/
|
|
- (SEL)action
|
|
{
|
|
return _action;
|
|
}
|
|
|
|
// Managing the Title
|
|
/*!
|
|
Sets the item's title.
|
|
@param aTitle the item's new title
|
|
*/
|
|
- (void)setTitle:(CPString)aTitle
|
|
{
|
|
_mnemonicLocation = CPNotFound;
|
|
|
|
if (_title == aTitle)
|
|
return;
|
|
|
|
_title = aTitle;
|
|
|
|
[_menuItemView setDirty];
|
|
|
|
[_menu itemChanged:self];
|
|
}
|
|
|
|
/*!
|
|
Returns the menu item's title.
|
|
*/
|
|
- (CPString)title
|
|
{
|
|
return _title;
|
|
}
|
|
|
|
/*!
|
|
Set's the item's text color
|
|
*/
|
|
- (void)setTextColor:(CPString)aColor
|
|
{
|
|
//FIXME IMPLEMENT
|
|
}
|
|
|
|
/*!
|
|
Sets the font for the text of this menu item
|
|
@param aFont the font for the menu item
|
|
*/
|
|
- (void)setFont:(CPFont)aFont
|
|
{
|
|
if ([_font isEqual:aFont])
|
|
return;
|
|
|
|
_font = aFont;
|
|
|
|
[_menu itemChanged:self];
|
|
|
|
[_menuItemView setDirty];
|
|
}
|
|
|
|
/*!
|
|
Returns the menu item's font
|
|
*/
|
|
- (CPFont)font
|
|
{
|
|
return _font;
|
|
}
|
|
|
|
/*
|
|
- (void)setAttributedTitle:(CPAttributedString)aTitle
|
|
{
|
|
}
|
|
|
|
- (CPAttributedString)attributedTitle
|
|
{
|
|
}
|
|
*/
|
|
|
|
// Managing the Tag
|
|
/*!
|
|
Sets the menu item's tag
|
|
@param aTag the tag for the item
|
|
*/
|
|
- (void)setTag:(int)aTag
|
|
{
|
|
_tag = aTag;
|
|
}
|
|
|
|
/*!
|
|
Returns the item's tag
|
|
*/
|
|
- (int)tag
|
|
{
|
|
return _tag;
|
|
}
|
|
|
|
/*!
|
|
Sets the state of the menu item. Possible states are:
|
|
<pre>
|
|
CPMixedState
|
|
CPOnState
|
|
CPOffState
|
|
</pre>
|
|
*/
|
|
- (void)setState:(int)aState
|
|
{
|
|
if (_state == aState)
|
|
return;
|
|
|
|
_state = aState;
|
|
|
|
[_menu itemChanged:self];
|
|
|
|
[_menuItemView setDirty];
|
|
}
|
|
|
|
/*!
|
|
Returns the menu item's current state. Possible states are:
|
|
<pre>
|
|
CPMixedState
|
|
CPOnState
|
|
CPOffState
|
|
</pre>
|
|
*/
|
|
- (int)state
|
|
{
|
|
return _state;
|
|
}
|
|
|
|
// Managing the Image
|
|
/*!
|
|
Sets the menu item's image
|
|
@param anImage the menu item's image
|
|
*/
|
|
- (void)setImage:(CPImage)anImage
|
|
{
|
|
if (_image == anImage)
|
|
return;
|
|
|
|
_image = anImage;
|
|
|
|
[_menuItemView setDirty];
|
|
|
|
[_menu itemChanged:self];
|
|
}
|
|
|
|
/*!
|
|
Returns the menu item's image
|
|
*/
|
|
- (CPImage)image
|
|
{
|
|
return _image;
|
|
}
|
|
|
|
/*!
|
|
Sets the menu item's alternate image
|
|
@param anImage the menu item's alternate image
|
|
*/
|
|
- (void)setAlternateImage:(CPImage)anImage
|
|
{
|
|
_alternateImage = anImage;
|
|
}
|
|
|
|
/*!
|
|
Returns the menu item's alternate image
|
|
*/
|
|
- (CPImage)alternateImage
|
|
{
|
|
return _alternateImage;
|
|
}
|
|
|
|
/*!
|
|
Sets the image that is shown when the
|
|
menu item is in the 'on' state.
|
|
@param anImage the image to show
|
|
*/
|
|
- (void)setOnStateImage:(CPImage)anImage
|
|
{
|
|
if (_onStateImage == anImage)
|
|
return;
|
|
|
|
_onStateImage = anImage;
|
|
[_menu itemChanged:self];
|
|
}
|
|
|
|
/*!
|
|
Returns the image shown when the menu item is in the 'on' state.
|
|
*/
|
|
- (CPImage)onStateImage
|
|
{
|
|
return _onStateImage;
|
|
}
|
|
|
|
/*!
|
|
Sets the image that is shown when the menu item is in the 'off' state.
|
|
@param anImage the image to show
|
|
*/
|
|
- (void)setOffStateImage:(CPImage)anImage
|
|
{
|
|
if (_offStateImage == anImage)
|
|
return;
|
|
|
|
_offStateImage = anImage;
|
|
[_menu itemChanged:self];
|
|
}
|
|
|
|
/*!
|
|
Returns the image shown when the menu item is in the 'off' state.
|
|
*/
|
|
- (CPImage)offStateImage
|
|
{
|
|
return _offStateImage;
|
|
}
|
|
|
|
/*!
|
|
Sets the image that is shown when the menu item is in the 'mixed' state.
|
|
@param anImage the image to show
|
|
*/
|
|
- (void)setMixedStateImage:(CPImage)anImage
|
|
{
|
|
if (_mixedStateImage == anImage)
|
|
return;
|
|
|
|
_mixedStateImage = anImage;
|
|
[_menu itemChanged:self];
|
|
}
|
|
|
|
/*!
|
|
Returns the image shown when the menu item is
|
|
in the 'mixed' state.
|
|
*/
|
|
- (CPImage)mixedStateImage
|
|
{
|
|
return _mixedStateImage;
|
|
}
|
|
|
|
// Managing Submenus
|
|
/*!
|
|
Sets the submenu for this item
|
|
@param aMenu the submenu
|
|
*/
|
|
- (void)setSubmenu:(CPMenu)aMenu
|
|
{
|
|
if (_submenu === aMenu)
|
|
return;
|
|
|
|
var supermenu = [_submenu supermenu];
|
|
|
|
if (supermenu)
|
|
[CPException raise:CPInvalidArgumentException
|
|
reason: @"Can't add submenu \"" + [aMenu title] + "\" to item \"" + [self title] + "\", because it is already submenu of \"" + [[aMenu supermenu] title] + "\""];
|
|
|
|
_submenu = aMenu;
|
|
|
|
if (_submenu)
|
|
{
|
|
[_submenu setSupermenu:_menu];
|
|
[_submenu setTitle:[self title]];
|
|
|
|
[self setTarget:_menu];
|
|
[self setAction:@selector(submenuAction:)];
|
|
}
|
|
else
|
|
{
|
|
[self setTarget:nil];
|
|
[self setAction:NULL];
|
|
}
|
|
|
|
[_menuItemView setDirty];
|
|
|
|
[_menu itemChanged:self];
|
|
}
|
|
|
|
/*!
|
|
Returns the submenu of the item. \c nil if there is no submenu.
|
|
*/
|
|
- (CPMenu)submenu
|
|
{
|
|
return _submenu;
|
|
}
|
|
|
|
/*!
|
|
Returns \c YES if the menu item has a submenu.
|
|
*/
|
|
- (BOOL)hasSubmenu
|
|
{
|
|
return _submenu ? YES : NO;
|
|
}
|
|
|
|
// Getting a Separator Item
|
|
|
|
/*!
|
|
Returns a new menu item separator.
|
|
*/
|
|
+ (CPMenuItem)separatorItem
|
|
{
|
|
var separatorItem = [[self alloc] initWithTitle:@"" action:nil keyEquivalent:nil];
|
|
|
|
separatorItem._isSeparator = YES;
|
|
|
|
return separatorItem;
|
|
}
|
|
|
|
/*!
|
|
Returns \c YES if the menu item is a separator.
|
|
*/
|
|
- (BOOL)isSeparatorItem
|
|
{
|
|
return _isSeparator;
|
|
}
|
|
|
|
// Managing the Owning Menu
|
|
/*!
|
|
Set the container menu of this item.
|
|
@param aMenu the item's container menu
|
|
*/
|
|
- (void)setMenu:(CPMenu)aMenu
|
|
{
|
|
_menu = aMenu;
|
|
}
|
|
|
|
/*!
|
|
Returns the container menu of this item
|
|
*/
|
|
- (CPMenu)menu
|
|
{
|
|
return _menu;
|
|
}
|
|
|
|
//
|
|
|
|
/*!
|
|
Sets the keyboard shortcut for this menu item
|
|
@param aString the keyboard shortcut
|
|
*/
|
|
- (void)setKeyEquivalent:(CPString)aString
|
|
{
|
|
_keyEquivalent = aString || @"";
|
|
}
|
|
|
|
/*!
|
|
Returns the keyboard shortcut for this menu item
|
|
*/
|
|
- (CPString)keyEquivalent
|
|
{
|
|
return _keyEquivalent;
|
|
}
|
|
|
|
/*!
|
|
Sets the modifier mask used for the item's keyboard shortcut.
|
|
Can be a combination of:
|
|
<pre>
|
|
CPShiftKeyMask
|
|
CPAlternateKeyMask
|
|
CPCommandKeyMask
|
|
CPControlKeyMask
|
|
</pre>
|
|
*/
|
|
- (void)setKeyEquivalentModifierMask:(unsigned)aMask
|
|
{
|
|
_keyEquivalentModifierMask = aMask;
|
|
}
|
|
|
|
/*!
|
|
Returns the item's keyboard shortcut modifier mask.
|
|
Can be a combination of:
|
|
<pre>
|
|
CPShiftKeyMask
|
|
CPAlternateKeyMask
|
|
CPCommandKeyMask
|
|
CPControlKeyMask
|
|
</pre>
|
|
*/
|
|
- (unsigned)keyEquivalentModifierMask
|
|
{
|
|
return _keyEquivalentModifierMask;
|
|
}
|
|
|
|
- (CPString)keyEquivalentStringRepresentation
|
|
{
|
|
if (![_keyEquivalent length])
|
|
return @"";
|
|
|
|
var string = _keyEquivalent.toUpperCase(),
|
|
needsShift = _keyEquivalentModifierMask & CPShiftKeyMask ||
|
|
(string === _keyEquivalent && _keyEquivalent.toLowerCase() !== _keyEquivalent.toUpperCase());
|
|
|
|
if ([CPMenuItemStringRepresentationDictionary objectForKey:string])
|
|
string = [CPMenuItemStringRepresentationDictionary objectForKey:string];
|
|
|
|
if (CPBrowserIsOperatingSystem(CPMacOperatingSystem))
|
|
{
|
|
if (_keyEquivalentModifierMask & CPCommandKeyMask)
|
|
string = "\u2318" + string;
|
|
|
|
if (needsShift)
|
|
string = "\u21E7" + string;
|
|
|
|
if (_keyEquivalentModifierMask & CPAlternateKeyMask)
|
|
string = "\u2325" + string;
|
|
|
|
if (_keyEquivalentModifierMask & CPControlKeyMask)
|
|
string = "\u2303" + string;
|
|
}
|
|
else
|
|
{
|
|
if (needsShift)
|
|
string = "Shift-" + string;
|
|
|
|
if (_keyEquivalentModifierMask & CPAlternateKeyMask)
|
|
string = "Alt-" + string;
|
|
|
|
if (_keyEquivalentModifierMask & CPControlKeyMask || _keyEquivalentModifierMask & CPCommandKeyMask)
|
|
string = "Ctrl-" + string;
|
|
}
|
|
|
|
return string;
|
|
}
|
|
|
|
// Managing Mnemonics
|
|
/*!
|
|
Sets the index of the mnemonic character in the title. The character
|
|
will be underlined and is used as a shortcut for navigation.
|
|
@param aLocation the index of the character in the title
|
|
*/
|
|
- (void)setMnemonicLocation:(unsigned)aLocation
|
|
{
|
|
_mnemonicLocation = aLocation;
|
|
}
|
|
|
|
/*!
|
|
Returns the index of the mnemonic character in the title.
|
|
*/
|
|
- (unsigned)mnemonicLocation
|
|
{
|
|
return _mnemonicLocation;
|
|
}
|
|
|
|
/*!
|
|
Sets the title of the menu item and the mnemonic character. The mnemonic character should be preceded by an '&'.
|
|
@param aTitle the title string with a denoted mnemonic
|
|
*/
|
|
- (void)setTitleWithMnemonicLocation:(CPString)aTitle
|
|
{
|
|
var location = [aTitle rangeOfString:@"&"].location;
|
|
|
|
if (location == CPNotFound)
|
|
[self setTitle:aTitle];
|
|
else
|
|
{
|
|
[self setTitle:[aTitle substringToIndex:location] + [aTitle substringFromIndex:location + 1]];
|
|
[self setMnemonicLocation:location];
|
|
}
|
|
}
|
|
|
|
/*!
|
|
Returns the menu items mnemonic character
|
|
*/
|
|
- (CPString)mnemonic
|
|
{
|
|
return _mnemonicLocation == CPNotFound ? @"" : [_title characterAtIndex:_mnemonicLocation];
|
|
}
|
|
|
|
// Managing Alternates
|
|
|
|
/*!
|
|
Sets whether this item is an alternate for the previous menu item.
|
|
@param isAlternate \c YES denotes that this menu item is an alternate
|
|
*/
|
|
- (void)setAlternate:(BOOL)isAlternate
|
|
{
|
|
_isAlternate = isAlternate;
|
|
}
|
|
|
|
/*!
|
|
Returns \c YES if the menu item is an alternate for the previous item.
|
|
*/
|
|
- (BOOL)isAlternate
|
|
{
|
|
return _isAlternate;
|
|
}
|
|
|
|
// Managing Indentation Levels
|
|
|
|
/*!
|
|
Sets the indentation level of the menu item. Must be a value between 0 and 15 (inclusive).
|
|
@param aLevel the item's new indentation level
|
|
@throws CPInvalidArgumentException if aLevel is less than 0
|
|
*/
|
|
- (void)setIndentationLevel:(unsigned)aLevel
|
|
{
|
|
if (aLevel < 0)
|
|
[CPException raise:CPInvalidArgumentException reason:"setIndentationLevel: argument must be greater than or equal to 0."];
|
|
|
|
_indentationLevel = MIN(15, aLevel);
|
|
}
|
|
|
|
/*!
|
|
Returns the menu item's indentation level. This is a value between 0 and 15 (inclusive).
|
|
*/
|
|
- (unsigned)indentationLevel
|
|
{
|
|
return _indentationLevel;
|
|
}
|
|
|
|
// Managing Tool Tips
|
|
/*!
|
|
Sets the tooltip for the menu item.
|
|
@param aToolTip the tool tip for the item
|
|
*/
|
|
- (void)setToolTip:(CPString)aToolTip
|
|
{
|
|
_toolTip = aToolTip;
|
|
}
|
|
|
|
/*!
|
|
Returns the item's tooltip
|
|
*/
|
|
- (CPString)toolTip
|
|
{
|
|
return _toolTip;
|
|
}
|
|
|
|
// Representing an Object
|
|
|
|
/*!
|
|
Sets the menu item's represented object. This is a kind of tag for the developer. Not a UI feature.
|
|
@param anObject the represented object
|
|
*/
|
|
- (void)setRepresentedObject:(id)anObject
|
|
{
|
|
_representedObject = anObject;
|
|
}
|
|
|
|
/*!
|
|
Returns the item's represented object.
|
|
*/
|
|
- (id)representedObject
|
|
{
|
|
return _representedObject;
|
|
}
|
|
|
|
// Managing the View
|
|
|
|
/*!
|
|
Sets the view for the menu item
|
|
@param aView the menu's item's view
|
|
*/
|
|
- (void)setView:(CPView)aView
|
|
{
|
|
if (_view === aView)
|
|
return;
|
|
|
|
_view = aView;
|
|
|
|
[_menuItemView setDirty];
|
|
|
|
[_menu itemChanged:self];
|
|
}
|
|
|
|
/*!
|
|
Returns the menu item's view
|
|
*/
|
|
- (CPView)view
|
|
{
|
|
return _view;
|
|
}
|
|
|
|
// Getting Highlighted Status
|
|
|
|
/*!
|
|
Returns \c YES if the menu item is highlighted.
|
|
*/
|
|
- (BOOL)isHighlighted
|
|
{
|
|
return [[self menu] highlightedItem] == self;
|
|
}
|
|
|
|
#pragma mark CPObject Overrides
|
|
|
|
/*!
|
|
Returns a copy of the item. The copy does not belong If the item has a submenu, it is NOT copied.
|
|
*/
|
|
- (id)copy
|
|
{
|
|
var item = [[CPMenuItem alloc] init];
|
|
|
|
// No point in going through accessors and doing lots of unnecessary state checking/updating
|
|
item._isSeparator = _isSeparator;
|
|
|
|
[item setTitle:_title];
|
|
[item setFont:_font];
|
|
[item setTarget:_target];
|
|
[item setAction:_action];
|
|
[item setEnabled:_isEnabled];
|
|
[item setHidden:_isHidden];
|
|
[item setTag:_tag];
|
|
[item setState:_state];
|
|
[item setImage:_image];
|
|
[item setAlternateImage:_alternateImage];
|
|
[item setOnStateImage:_onStateImage];
|
|
[item setOffStateImage:_offStateImage];
|
|
[item setMixedStateImage:_mixedStateImage];
|
|
[item setKeyEquivalent:_keyEquivalent];
|
|
[item setKeyEquivalentModifierMask:_keyEquivalentModifierMask];
|
|
[item setMnemonicLocation:_mnemonicLocation];
|
|
[item setAlternate:_isAlternate];
|
|
[item setIndentationLevel:_indentationLevel];
|
|
[item setToolTip:_toolTip];
|
|
[item setRepresentedObject:_representedObject];
|
|
|
|
return item;
|
|
}
|
|
|
|
- (id)mutableCopy
|
|
{
|
|
return [self copy];
|
|
}
|
|
|
|
#pragma mark Internal
|
|
|
|
/*
|
|
@ignore
|
|
*/
|
|
- (id)_menuItemView
|
|
{
|
|
if (!_menuItemView)
|
|
_menuItemView = [[_CPMenuItemView alloc] initWithFrame:CGRectMakeZero() forMenuItem:self];
|
|
|
|
return _menuItemView;
|
|
}
|
|
|
|
- (BOOL)_isSelectable
|
|
{
|
|
return ![self submenu] || [self action] !== @selector(submenuAction:) || [self target] !== [self menu];
|
|
}
|
|
|
|
- (BOOL)_isMenuBarButton
|
|
{
|
|
return ![self submenu] && [self menu] === [CPApp mainMenu];
|
|
}
|
|
|
|
- (CPString)description
|
|
{
|
|
return [super description] + @" target: " + [self target] + @" action: " + CPStringFromSelector([self action]);
|
|
}
|
|
|
|
@end
|
|
|
|
#pragma mark -
|
|
|
|
@implementation CPMenuItem (CSSTheming)
|
|
|
|
- (void)_setThemeIncludingDescendants:(CPTheme)aTheme
|
|
{
|
|
[_view _setThemeIncludingDescendants:aTheme];
|
|
[_menuItemView _setThemeIncludingDescendants:aTheme];
|
|
}
|
|
|
|
@end
|
|
|
|
#pragma mark -
|
|
|
|
var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey",
|
|
|
|
CPMenuItemTitleKey = @"CPMenuItemTitleKey",
|
|
CPMenuItemTargetKey = @"CPMenuItemTargetKey",
|
|
CPMenuItemActionKey = @"CPMenuItemActionKey",
|
|
|
|
CPMenuItemIsEnabledKey = @"CPMenuItemIsEnabledKey",
|
|
CPMenuItemIsHiddenKey = @"CPMenuItemIsHiddenKey",
|
|
|
|
CPMenuItemTagKey = @"CPMenuItemTagKey",
|
|
CPMenuItemStateKey = @"CPMenuItemStateKey",
|
|
|
|
CPMenuItemImageKey = @"CPMenuItemImageKey",
|
|
CPMenuItemAlternateImageKey = @"CPMenuItemAlternateImageKey",
|
|
|
|
CPMenuItemSubmenuKey = @"CPMenuItemSubmenuKey",
|
|
CPMenuItemMenuKey = @"CPMenuItemMenuKey",
|
|
|
|
CPMenuItemKeyEquivalentKey = @"CPMenuItemKeyEquivalentKey",
|
|
CPMenuItemKeyEquivalentModifierMaskKey = @"CPMenuItemKeyEquivalentModifierMaskKey",
|
|
|
|
CPMenuItemIndentationLevelKey = @"CPMenuItemIndentationLevelKey",
|
|
|
|
CPMenuItemRepresentedObjectKey = @"CPMenuItemRepresentedObjectKey",
|
|
CPMenuItemViewKey = @"CPMenuItemViewKey";
|
|
|
|
#define DEFAULT_VALUE(aKey, aDefaultValue) [aCoder containsValueForKey:(aKey)] ? [aCoder decodeObjectForKey:(aKey)] : (aDefaultValue)
|
|
#define ENCODE_IFNOT(aKey, aValue, aDefaultValue) if ((aValue) !== (aDefaultValue)) [aCoder encodeObject:(aValue) forKey:(aKey)];
|
|
|
|
@implementation CPMenuItem (CPCoding)
|
|
/*!
|
|
Initializes the menu item from a coder.
|
|
@param aCoder the coder from which to initialize
|
|
@return the initialized menu item
|
|
*/
|
|
- (id)initWithCoder:(CPCoder)aCoder
|
|
{
|
|
self = [super init];
|
|
|
|
if (self)
|
|
{
|
|
_changeCount = 0;
|
|
_isSeparator = [aCoder containsValueForKey:CPMenuItemIsSeparatorKey] && [aCoder decodeBoolForKey:CPMenuItemIsSeparatorKey];
|
|
|
|
_title = [aCoder decodeObjectForKey:CPMenuItemTitleKey];
|
|
|
|
// _font;
|
|
|
|
_target = [aCoder decodeObjectForKey:CPMenuItemTargetKey];
|
|
_action = [aCoder decodeObjectForKey:CPMenuItemActionKey];
|
|
|
|
_isEnabled = DEFAULT_VALUE(CPMenuItemIsEnabledKey, YES);
|
|
_isHidden = [aCoder decodeBoolForKey:CPMenuItemIsHiddenKey];
|
|
_tag = [aCoder decodeIntForKey:CPMenuItemTagKey];
|
|
_state = [aCoder decodeIntForKey:CPMenuItemStateKey];
|
|
|
|
_image = [aCoder decodeObjectForKey:CPMenuItemImageKey];
|
|
_alternateImage = [aCoder decodeObjectForKey:CPMenuItemAlternateImageKey];
|
|
// CPImage _onStateImage;
|
|
// CPImage _offStateImage;
|
|
// CPImage _mixedStateImage;
|
|
|
|
// This order matters because setSubmenu: needs _menu to be around.
|
|
_menu = [aCoder decodeObjectForKey:CPMenuItemMenuKey];
|
|
[self setSubmenu:[aCoder decodeObjectForKey:CPMenuItemSubmenuKey]];
|
|
|
|
_keyEquivalent = [aCoder decodeObjectForKey:CPMenuItemKeyEquivalentKey] || @"";
|
|
_keyEquivalentModifierMask = [aCoder decodeIntForKey:CPMenuItemKeyEquivalentModifierMaskKey];
|
|
|
|
// int _mnemonicLocation;
|
|
|
|
// BOOL _isAlternate;
|
|
|
|
[self setIndentationLevel:[aCoder decodeIntForKey:CPMenuItemIndentationLevelKey]];
|
|
|
|
// CPString _toolTip;
|
|
|
|
_representedObject = [aCoder decodeObjectForKey:CPMenuItemRepresentedObjectKey];
|
|
_view = [aCoder decodeObjectForKey:CPMenuItemViewKey];
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
/*!
|
|
Writes the menu item out to a coder.
|
|
@param aCoder the coder to write the menu item out to
|
|
*/
|
|
- (void)encodeWithCoder:(CPCoder)aCoder
|
|
{
|
|
if (_isSeparator)
|
|
[aCoder encodeBool:_isSeparator forKey:CPMenuItemIsSeparatorKey];
|
|
|
|
[aCoder encodeObject:_title forKey:CPMenuItemTitleKey];
|
|
|
|
[aCoder encodeObject:_target forKey:CPMenuItemTargetKey];
|
|
[aCoder encodeObject:_action forKey:CPMenuItemActionKey];
|
|
|
|
ENCODE_IFNOT(CPMenuItemIsEnabledKey, _isEnabled, YES);
|
|
ENCODE_IFNOT(CPMenuItemIsHiddenKey, _isHidden, NO);
|
|
|
|
ENCODE_IFNOT(CPMenuItemTagKey, _tag, 0);
|
|
ENCODE_IFNOT(CPMenuItemStateKey, _state, CPOffState);
|
|
|
|
ENCODE_IFNOT(CPMenuItemImageKey, _image, nil);
|
|
ENCODE_IFNOT(CPMenuItemAlternateImageKey, _alternateImage, nil);
|
|
|
|
ENCODE_IFNOT(CPMenuItemSubmenuKey, _submenu, nil);
|
|
ENCODE_IFNOT(CPMenuItemMenuKey, _menu, nil);
|
|
|
|
if (_keyEquivalent && _keyEquivalent.length)
|
|
[aCoder encodeObject:_keyEquivalent forKey:CPMenuItemKeyEquivalentKey];
|
|
|
|
if (_keyEquivalentModifierMask)
|
|
[aCoder encodeObject:_keyEquivalentModifierMask forKey:CPMenuItemKeyEquivalentModifierMaskKey];
|
|
|
|
if (_indentationLevel > 0)
|
|
[aCoder encodeInt:_indentationLevel forKey:CPMenuItemIndentationLevelKey];
|
|
|
|
ENCODE_IFNOT(CPMenuItemRepresentedObjectKey, _representedObject, nil);
|
|
ENCODE_IFNOT(CPMenuItemViewKey, _view, nil);
|
|
}
|
|
|
|
@end
|