Files
cappuccino/AppKit/CPMenuItem.j
T

1279 lines
29 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 <AppKit/CPImage.j>
@import <AppKit/CPMenu.j>
@import <AppKit/CPView.j>
/*!
@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;
_CPMenuItemView _menuItemView;
}
- (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)
{
_isSeparator = NO;
_title = aTitle;
_action = anAction;
_isEnabled = YES;
_tag = 0;
_state = CPOffState;
_keyEquivalent = aKeyEquivalent || @"";
_keyEquivalentModifierMask = CPPlatformActionKeyMask;
_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 ([_menu autoenablesItems])
return;
_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 == 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 Subemenus
/*!
Sets the submenu for this item
@param aMenu the submenu
*/
- (void)setSubmenu:(CPMenu)aMenu
{
var supermenu = [_submenu supermenu];
if (supermenu == self)
return;
if (supermenu)
return alert("bad");
[_submenu setSupermenu:_menu];
_submenu = aMenu;
[_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;
}
// 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 chracter 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 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;
}
//
/*
@ignore
*/
- (id)_menuItemView
{
if (!_menuItemView)
_menuItemView = [[_CPMenuItemView alloc] initWithFrame:CGRectMakeZero() forMenuItem:self];
return _menuItemView;
}
@end
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",
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)
{
_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 = DEFAULT_VALUE(CPMenuItemIsHiddenKey, NO);
_tag = DEFAULT_VALUE(CPMenuItemTagKey, 0);
_state = DEFAULT_VALUE(CPMenuItemStateKey, CPOffState);
// int _state;
_image = DEFAULT_VALUE(CPMenuItemImageKey, nil);
_alternateImage = DEFAULT_VALUE(CPMenuItemAlternateImageKey, nil);
// CPImage _onStateImage;
// CPImage _offStateImage;
// CPImage _mixedStateImage;
_submenu = DEFAULT_VALUE(CPMenuItemSubmenuKey, nil);
_menu = DEFAULT_VALUE(CPMenuItemMenuKey, nil);
_keyEquivalent = [aCoder decodeObjectForKey:CPMenuItemKeyEquivalentKey] || @"";
_keyEquivalentModifierMask = [aCoder decodeObjectForKey:CPMenuItemKeyEquivalentModifierMaskKey] || 0;
// int _mnemonicLocation;
// BOOL _isAlternate;
// int _indentationLevel;
// CPString _toolTip;
_representedObject = DEFAULT_VALUE(CPMenuItemRepresentedObjectKey, nil);
_view = DEFAULT_VALUE(CPMenuItemViewKey, nil);
}
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];
ENCODE_IFNOT(CPMenuItemRepresentedObjectKey, _representedObject, nil);
ENCODE_IFNOT(CPMenuItemViewKey, _view, nil);
}
@end
var LEFT_MARGIN = 3.0,
RIGHT_MARGIN = 16.0,
STATE_COLUMN_WIDTH = 14.0,
INDENTATION_WIDTH = 17.0,
VERTICAL_MARGIN = 4.0;
var _CPMenuItemSelectionColor = nil,
_CPMenuItemTextShadowColor = nil,
_CPMenuItemDefaultStateImages = [],
_CPMenuItemDefaultStateHighlightedImages = [];
/*
@ignore
*/
@implementation _CPMenuItemView : CPView
{
CPMenuItem _menuItem;
CPFont _font;
CPColor _textColor;
CPColor _textShadowColor;
CPColor _activateColor;
CPColor _activateShadowColor;
CGSize _minSize;
BOOL _isDirty;
BOOL _showsStateColumn;
BOOL _belongsToMenuBar;
CPImageView _stateView;
_CPImageAndTextView _imageAndTextView;
CPView _submenuView;
}
+ (void)initialize
{
if (self != [_CPMenuItemView class])
return;
_CPMenuItemSelectionColor = [CPColor colorWithCalibratedRed:95.0 / 255.0 green:131.0 / 255.0 blue:185.0 / 255.0 alpha:1.0];
_CPMenuItemTextShadowColor = [CPColor colorWithCalibratedRed:26.0 / 255.0 green: 73.0 / 255.0 blue:109.0 / 255.0 alpha:1.0]
var bundle = [CPBundle bundleForClass:self];
_CPMenuItemDefaultStateImages[CPOffState] = nil;
_CPMenuItemDefaultStateHighlightedImages[CPOffState] = nil;
_CPMenuItemDefaultStateImages[CPOnState] = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPMenuItem/CPMenuItemOnState.png"] size:CGSizeMake(14.0, 14.0)];
_CPMenuItemDefaultStateHighlightedImages[CPOnState] = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPMenuItem/CPMenuItemOnStateHighlighted.png"] size:CGSizeMake(14.0, 14.0)];
_CPMenuItemDefaultStateImages[CPMixedState] = nil;
_CPMenuItemDefaultStateHighlightedImages[CPMixedState] = nil;
}
+ (float)leftMargin
{
return LEFT_MARGIN + STATE_COLUMN_WIDTH;
}
- (id)initWithFrame:(CGRect)aFrame forMenuItem:(CPMenuItem)aMenuItem
{
self = [super initWithFrame:aFrame];
if (self)
{
_menuItem = aMenuItem;
_showsStateColumn = YES;
_isDirty = YES;
[self setAutoresizingMask:CPViewWidthSizable];
[self synchronizeWithMenuItem];
}
return self;
}
- (CGSize)minSize
{
return _minSize;
}
- (void)setDirty
{
_isDirty = YES;
}
- (void)synchronizeWithMenuItem
{
if (!_isDirty)
return;
_isDirty = NO;
var view = [_menuItem view];
if ([_menuItem isSeparatorItem])
{
var line = [[CPView alloc] initWithFrame:CGRectMake(0.0, 5.0, 10.0, 1.0)];
view = [[CPView alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, 10.0)];
[view setAutoresizingMask:CPViewWidthSizable];
[line setAutoresizingMask:CPViewWidthSizable];
[line setBackgroundColor:[CPColor lightGrayColor]];
[view addSubview:line];
}
if (view)
{
[_imageAndTextView removeFromSuperview];
_imageAndTextView = nil;
[_stateView removeFromSuperview];
_stateView = nil;
[_submenuView removeFromSuperview];
_submenuView = nil;
_minSize = [view frame].size;
[self setFrameSize:_minSize];
[self addSubview:view];
return;
}
// State Column
var x = _belongsToMenuBar ? 0.0 : (LEFT_MARGIN + [_menuItem indentationLevel] * INDENTATION_WIDTH);
if (_showsStateColumn)
{
if (!_stateView)
{
_stateView = [[CPImageView alloc] initWithFrame:CGRectMake(x, (CGRectGetHeight([self frame]) - STATE_COLUMN_WIDTH) / 2.0, STATE_COLUMN_WIDTH, STATE_COLUMN_WIDTH)];
[_stateView setAutoresizingMask:CPViewMinYMargin | CPViewMaxYMargin];
[self addSubview:_stateView];
}
var state = [_menuItem state];
switch (state)
{
case CPOffState:
case CPOnState:
case CPMixedState: [_stateView setImage:_CPMenuItemDefaultStateImages[state]];
break;
default: [_stateView setImage:nil];
}
x += STATE_COLUMN_WIDTH;
}
else
{
[_stateView removeFromSuperview];
_stateView = nil;
}
// Image and Title
if (!_imageAndTextView)
{
_imageAndTextView = [[_CPImageAndTextView alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, 0.0)];
[_imageAndTextView setImagePosition:CPImageLeft];
[_imageAndTextView setTextShadowOffset:CGSizeMake(0.0, 1.0)];
[self addSubview:_imageAndTextView];
}
var font = [_menuItem font];
if (!font)
font = _font;
[_imageAndTextView setFont:font];
[_imageAndTextView setVerticalAlignment:CPCenterVerticalTextAlignment];
[_imageAndTextView setImage:[_menuItem image]];
[_imageAndTextView setText:[_menuItem title]];
[_imageAndTextView setTextColor:[self textColor]];
[_imageAndTextView setTextShadowColor:[self textShadowColor]];
[_imageAndTextView setTextShadowOffset:CGSizeMake(0, 1)];
[_imageAndTextView setFrameOrigin:CGPointMake(x, VERTICAL_MARGIN)];
[_imageAndTextView sizeToFit];
var frame = [_imageAndTextView frame];
// frame.size.height += 1.0;
// [_imageAndTextView setFrame:frame];
frame.size.height += 2 * VERTICAL_MARGIN;
x += CGRectGetWidth(frame);
// Submenu Arrow
if ([_menuItem hasSubmenu])
{
x += 3.0;
if (!_submenuView)
{
_submenuView = [[_CPMenuItemArrowView alloc] initWithFrame:CGRectMake(0.0, 0.0, 10.0, 10.0)];
[self addSubview:_submenuView];
}
[_submenuView setHidden:NO];
[_submenuView setColor:_belongsToMenuBar ? [self textColor] : nil];
[_submenuView setFrameOrigin:CGPointMake(x, (CGRectGetHeight(frame) - 10.0) / 2.0)];
x += 10.0;
}
else
[_submenuView setHidden:YES];
_minSize = CGSizeMake(x + (_belongsToMenuBar ? 0.0 : RIGHT_MARGIN) + 3.0, CGRectGetHeight(frame));
[self setFrameSize:_minSize];
}
- (CGFloat)overlapOffsetWidth
{
return LEFT_MARGIN + ([[_menuItem menu] showsStateColumn] ? STATE_COLUMN_WIDTH : 0.0);
}
- (void)setShowsStateColumn:(BOOL)shouldShowStateColumn
{
_showsStateColumn = shouldShowStateColumn;
}
- (void)setBelongsToMenuBar:(BOOL)shouldBelongToMenuBar
{
_belongsToMenuBar = shouldBelongToMenuBar;
}
- (void)highlight:(BOOL)shouldHighlight
{
// ASSERT(![_menuItem view]);
if (_belongsToMenuBar)
[_imageAndTextView setImage:shouldHighlight ? [_menuItem alternateImage] : [_menuItem image]];
else if ([_menuItem isEnabled])
{
if (shouldHighlight)
{
[self setBackgroundColor:_CPMenuItemSelectionColor];
[_imageAndTextView setTextColor:[CPColor whiteColor]];
[_imageAndTextView setTextShadowColor:_CPMenuItemTextShadowColor];
}
else
{
[self setBackgroundColor:nil];
[_imageAndTextView setTextColor:[self textColor]];
[_imageAndTextView setTextShadowColor:[self textShadowColor]];
}
var state = [_menuItem state];
switch (state)
{
case CPOffState:
case CPOnState:
case CPMixedState: [_stateView setImage:shouldHighlight ? _CPMenuItemDefaultStateHighlightedImages[state] : _CPMenuItemDefaultStateImages[state]];
break;
default: [_stateView setImage:nil];
}
}
}
- (void)activate:(BOOL)shouldActivate
{
[_imageAndTextView setImage:[_menuItem image]];
if (shouldActivate)
{
[_imageAndTextView setTextColor:[self activateColor] || [CPColor whiteColor]];
[_imageAndTextView setTextShadowColor:[self activateShadowColor] || [CPColor blackColor]];
[_submenuView setColor:[self activateColor] || [CPColor whiteColor]];
}
else
{
[_imageAndTextView setTextColor:[self textColor]];
[_imageAndTextView setTextShadowColor:[self textShadowColor]];
[_submenuView setColor:[self textColor]];
}
}
- (BOOL)eventOnSubmenu:(CPEvent)anEvent
{
if (![_menuItem hasSubmenu])
return NO;
return CGRectContainsPoint([_submenuView frame], [self convertPoint:[anEvent locationInWindow] fromView:nil]);
}
- (BOOL)isHidden
{
return [_menuItem isHidden];
}
- (CPMenuItem)menuItem
{
return _menuItem;
}
- (void)setFont:(CPFont)aFont
{
if (_font == aFont)
return;
_font = aFont;
[self setDirty];
}
- (void)setTextColor:(CPColor)aColor
{
if (_textColor == aColor)
return;
_textColor = aColor;
[_imageAndTextView setTextColor:[self textColor]];
[_submenuView setColor:[self textColor]];
}
- (CPColor)textColor
{
return [_menuItem isEnabled] ? (_textColor ? _textColor : [CPColor colorWithCalibratedRed:70.0 / 255.0 green:69.0 / 255.0 blue:69.0 / 255.0 alpha:1.0]) : [CPColor lightGrayColor];
}
- (void)setTextShadowColor:(CPColor)aColor
{
if (_textShadowColor == aColor)
return;
_textShadowColor = aColor;
[_imageAndTextView setTextShadowColor:[self textShadowColor]];
//[_submenuView setColor:[self textColor]];
}
- (CPColor)textShadowColor
{
return [_menuItem isEnabled] ? (_textShadowColor ? _textShadowColor : [CPColor colorWithWhite:1.0 alpha:0.8]) : [CPColor colorWithWhite:0.8 alpha:0.8];
}
- (void)setActivateColor:(CPColor)aColor
{
_activateColor = aColor;
}
- (CPColor)activateColor
{
return _activateColor;
}
- (void)setActivateShadowColor:(CPColor)aColor
{
_activateShadowColor = aColor;
}
- (CPColor)activateShadowColor
{
return _activateShadowColor;
}
@end
@implementation _CPMenuItemArrowView : CPView
{
CPColor _color;
}
- (void)setColor:(CPColor)aColor
{
if (_color == aColor)
return;
_color = aColor;
[self setNeedsDisplay:YES];
}
- (void)drawRect:(CGRect)aRect
{
var context = [[CPGraphicsContext currentContext] graphicsPort];
CGContextBeginPath(context);
CGContextMoveToPoint(context, 1.0, 4.0);
CGContextAddLineToPoint(context, 9.0, 4.0);
CGContextAddLineToPoint(context, 5.0, 8.0);
CGContextAddLineToPoint(context, 1.0, 4.0);
CGContextClosePath(context);
CGContextSetFillColor(context, _color);
CGContextFillPath(context);
}
@end