33 var _CPMenuBarVisible = NO,
34 _CPMenuBarTitle =
@"",
35 _CPMenuBarAttributes = nil,
36 _CPMenuBarSharedWindow = nil;
58 BOOL _autoenablesItems;
59 BOOL _showsStateColumn;
63 int _highlightedIndex;
64 _CPMenuWindow _menuWindow;
73 if (
self !== [
CPMenu class])
76 [[
self class] setMenuBarAttributes:@{}];
79 + (BOOL)menuBarVisible
81 return _CPMenuBarVisible;
84 + (void)setMenuBarVisible:(BOOL)menuBarShouldBeVisible
86 if (_CPMenuBarVisible === menuBarShouldBeVisible)
89 _CPMenuBarVisible = menuBarShouldBeVisible;
94 if (menuBarShouldBeVisible)
96 if (!_CPMenuBarSharedWindow)
97 _CPMenuBarSharedWindow = [[_CPMenuBarWindow alloc] init];
99 [_CPMenuBarSharedWindow setMenu:[CPApp mainMenu]];
101 [_CPMenuBarSharedWindow setTitle:_CPMenuBarTitle];
102 [_CPMenuBarSharedWindow setIconImage:[[
CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-icon-image" forClass:_CPMenuView]];
103 [_CPMenuBarSharedWindow setIconImageAlphaValue:[[
CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-icon-image-alpha-value" forClass:_CPMenuView]];
105 [_CPMenuBarSharedWindow setColor:[_CPMenuBarAttributes objectForKey:@"CPMenuBarBackgroundColor"]];
106 [_CPMenuBarSharedWindow setTextColor:[_CPMenuBarAttributes objectForKey:@"CPMenuBarTextColor"]];
107 [_CPMenuBarSharedWindow setTitleColor:[_CPMenuBarAttributes objectForKey:@"CPMenuBarTitleColor"]];
108 [_CPMenuBarSharedWindow setTextShadowColor:[_CPMenuBarAttributes objectForKey:@"CPMenuBarTextShadowColor"]];
109 [_CPMenuBarSharedWindow setTitleShadowColor:[_CPMenuBarAttributes objectForKey:@"CPMenuBarTitleShadowColor"]];
110 [_CPMenuBarSharedWindow setHighlightColor:[_CPMenuBarAttributes objectForKey:@"CPMenuBarHighlightColor"]];
111 [_CPMenuBarSharedWindow setHighlightTextColor:[_CPMenuBarAttributes objectForKey:@"CPMenuBarHighlightTextColor"]];
112 [_CPMenuBarSharedWindow setHighlightTextShadowColor:[_CPMenuBarAttributes objectForKey:@"CPMenuBarHighlightTextShadowColor"]];
114 [_CPMenuBarSharedWindow orderFront:self];
117 [_CPMenuBarSharedWindow orderOut:self];
127 _CPMenuBarTitle = aTitle;
128 [_CPMenuBarSharedWindow setTitle:_CPMenuBarTitle];
133 return _CPMenuBarTitle;
138 _CPMenuBarImage = anImage;
139 [_CPMenuBarSharedWindow setIconImage:anImage];
144 return _CPMenuBarImage;
147 + (void)_setOrRemoveMenuBarAttribute:(
id)aValue forKey:(
id)aKey
150 [_CPMenuBarAttributes removeObjectForKey:aKey];
152 [_CPMenuBarAttributes setObject:aValue forKey:aKey];
157 if (_CPMenuBarAttributes == attributes)
160 _CPMenuBarAttributes = [attributes
copy];
162 var textColor = [attributes
objectForKey:@"CPMenuBarTextColor"],
163 titleColor = [attributes
objectForKey:@"CPMenuBarTitleColor"],
164 textShadowColor = [attributes
objectForKey:@"CPMenuBarTextShadowColor"],
165 titleShadowColor = [attributes
objectForKey:@"CPMenuBarTitleShadowColor"],
166 highlightColor = [attributes
objectForKey:@"CPMenuBarHighlightColor"],
167 highlightTextColor = [attributes
objectForKey:@"CPMenuBarHighlightTextColor"],
168 highlightTextShadowColor = [attributes
objectForKey:@"CPMenuBarHighlightTextShadowColor"];
170 if (!textColor && titleColor)
171 [_CPMenuBarAttributes setObject:titleColor forKey:@"CPMenuBarTextColor"];
173 else if (textColor && !titleColor)
174 [_CPMenuBarAttributes setObject:textColor forKey:@"CPMenuBarTitleColor"];
176 else if (!textColor && !titleColor)
178 [
self _setOrRemoveMenuBarAttribute:[[
CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-text-color" forClass:_CPMenuView] forKey:@"CPMenuBarTextColor"];
179 [
self _setOrRemoveMenuBarAttribute:[[
CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-title-color" forClass:_CPMenuView] forKey:@"CPMenuBarTitleColor"];
182 if (!textShadowColor && titleShadowColor)
183 [_CPMenuBarAttributes setObject:titleShadowColor forKey:@"CPMenuBarTextShadowColor"];
185 else if (textShadowColor && !titleShadowColor)
186 [_CPMenuBarAttributes setObject:textShadowColor forKey:@"CPMenuBarTitleShadowColor"];
188 else if (!textShadowColor && !titleShadowColor)
190 [
self _setOrRemoveMenuBarAttribute:[[
CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-text-shadow-color" forClass:_CPMenuView] forKey:@"CPMenuBarTextShadowColor"];
191 [
self _setOrRemoveMenuBarAttribute:[[
CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-title-shadow-color" forClass:_CPMenuView] forKey:@"CPMenuBarTitleShadowColor"];
195 [
self _setOrRemoveMenuBarAttribute:[[
CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-highlight-color" forClass:_CPMenuView] forKey:@"CPMenuBarHighlightColor"];
197 if (!highlightTextColor)
198 [
self _setOrRemoveMenuBarAttribute:[[
CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-highlight-text-color" forClass:_CPMenuView] forKey:@"CPMenuBarHighlightTextColor"];
200 if (!highlightTextShadowColor)
201 [
self _setOrRemoveMenuBarAttribute:[[
CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-highlight-text-shadow-color" forClass:_CPMenuView] forKey:@"CPMenuBarHighlightTextShadowColor"];
203 if (_CPMenuBarSharedWindow)
205 [_CPMenuBarSharedWindow setColor:[_CPMenuBarAttributes objectForKey:@"CPMenuBarBackgroundColor"]];
206 [_CPMenuBarSharedWindow setTextColor:[_CPMenuBarAttributes objectForKey:@"CPMenuBarTextColor"]];
207 [_CPMenuBarSharedWindow setTitleColor:[_CPMenuBarAttributes objectForKey:@"CPMenuBarTitleColor"]];
208 [_CPMenuBarSharedWindow setTextShadowColor:[_CPMenuBarAttributes objectForKey:@"CPMenuBarTextShadowColor"]];
209 [_CPMenuBarSharedWindow setTitleShadowColor:[_CPMenuBarAttributes objectForKey:@"CPMenuBarTitleShadowColor"]];
210 [_CPMenuBarSharedWindow setHighlightColor:[_CPMenuBarAttributes objectForKey:@"CPMenuBarHighlightColor"]];
211 [_CPMenuBarSharedWindow setHighlightTextColor:[_CPMenuBarAttributes objectForKey:@"CPMenuBarHighlightTextColor"]];
212 [_CPMenuBarSharedWindow setHighlightTextShadowColor:[_CPMenuBarAttributes objectForKey:@"CPMenuBarHighlightTextShadowColor"]];
218 return _CPMenuBarAttributes;
221 + (void)_setMenuBarIconImageAlphaValue:(
float)anAlphaValue
223 _CPMenuBarIconImageAlphaValue = anAlphaValue;
224 [_CPMenuBarSharedWindow setIconImageAlphaValue:anAlphaValue];
227 - (float)menuBarHeight
229 if (
self === [
CPApp mainMenu])
235 + (float)menuBarHeight
237 return [[
CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-height" forClass:_CPMenuView];
255 _autoenablesItems = YES;
256 _showsStateColumn = YES;
275 - (void)insertItem:(
CPMenuItem)aMenuItem atIndex:(CPUInteger)anIndex
290 var item = [[
CPMenuItem alloc] initWithTitle:aTitle action:anAction keyEquivalent:aKeyEquivalent];
332 - (void)removeItemAtIndex:(CPUInteger)anIndex
343 - (void)removeAllItems
345 var count = [_items count];
350 [_items[count] setMenu:nil];
352 [
self _highlightItemAtIndex:CPNotFound];
355 [
self willChangeValueForKey:@"items"];
357 [
self didChangeValueForKey:@"items"];
371 if ([aMenuItem
menu] !==
self || !_items)
374 [aMenuItem setValue:[aMenuItem valueForKey:@"changeCount"] + 1 forKey:@"changeCount"];
379 userInfo:@{ @"CPMenuItemIndex": [_items indexOfObjectIdenticalTo:aMenuItem] }];
395 return _items[index];
410 return _items[index];
419 return [_items objectAtIndex:anIndex];
425 - (unsigned)numberOfItems
427 return [_items count];
446 if ([aMenuItem
menu] !==
self)
449 return [_items indexOfObjectIdenticalTo:aMenuItem];
460 count = _items.length;
462 for (; index < count; ++index)
463 if ([_items[index] title] === aTitle)
474 - (int)indexOfItemWithTag:(
int)aTag
477 count = _items.length;
479 for (; index < count; ++index)
480 if ([_items[index]
tag] == aTag)
492 - (int)indexOfItemWithTarget:(
id)aTarget andAction:(
SEL)anAction
495 count = _items.length;
497 for (; index < count; ++index)
499 var item = _items[index];
501 if ([item target] == aTarget && (!anAction || [item action] == anAction))
513 - (int)indexOfItemWithRepresentedObject:(
id)anObject
516 count = _items.length;
518 for (; index < count; ++index)
519 if ([[_items[index] representedObject]
isEqual:anObject])
530 - (int)indexOfItemWithSubmenu:(
CPMenu)aMenu
533 count = _items.length;
535 for (; index < count; ++index)
536 if ([_items[index] submenu] == aMenu)
550 [aMenuItem setTarget:aMenuItem];
551 [aMenuItem setAction:@selector(submenuAction:)];
553 [aMenuItem setSubmenu:aMenu];
562 - (void)submenuAction:(
id)aSender
589 return !_supermenu ||
self == [CPApp mainMenu];
597 - (void)setAutoenablesItems:(BOOL)aFlag
599 _autoenablesItems = aFlag;
605 - (BOOL)autoenablesItems
607 return _autoenablesItems;
618 if (!_autoenablesItems)
623 for (var i = 0; i < [items count]; i++)
625 var item = [items objectAtIndex:i];
627 if ([item hasSubmenu])
631 var binder = [
CPBinder getBinding:CPEnabledBinding forObject:item];
635 [binder setValueFor:CPEnabledBinding];
639 var validator = [CPApp targetForAction:[item action] to:[item target] from:item],
640 shouldBeEnabled = YES;
646 if ([item action] || [item target])
647 shouldBeEnabled = NO;
651 var info = [
CPBinder infoForBinding:CPTargetBinding forObject:item];
655 var
object = [info objectForKey:CPObservedObjectKey],
656 keyPath = [info objectForKey:CPObservedKeyPathKey],
657 options = [info objectForKey:CPOptionsKey],
658 target = [object valueForKeyPath:keyPath],
659 selector = [options valueForKey:CPSelectorNameBindingOption];
662 shouldBeEnabled = NO;
666 else if (![validator respondsToSelector:[item action]])
667 shouldBeEnabled = NO;
668 else if ([validator respondsToSelector:
@selector(validateMenuItem:)])
669 shouldBeEnabled = [validator validateMenuItem:item];
670 else if ([validator respondsToSelector:
@selector(validateUserInterfaceItem:)])
671 shouldBeEnabled = [validator validateUserInterfaceItem:item];
673 [item setEnabled:shouldBeEnabled];
676 [[_menuWindow _menuView] tile];
697 - (void)setMinimumWidth:(
float)aMinimumWidth
699 _minimumWidth = aMinimumWidth;
702 - (float)minimumWidth
704 return _minimumWidth;
707 - (void)_performActionOfHighlightedItemChain
709 var highlightedItem = [
self highlightedItem];
711 while ([highlightedItem submenu] && [highlightedItem action] ===
@selector(submenuAction:))
712 highlightedItem = [[highlightedItem submenu] highlightedItem];
717 if (highlightedItem && [highlightedItem isEnabled])
720 var binding = [
CPBinder getBinding:CPTargetBinding forObject:highlightedItem];
721 [binding invokeAction];
723 [CPApp sendAction:[highlightedItem action] to:[highlightedItem target] from:highlightedItem];
728 + (CGRect)_constraintRectForView:(
CPView)aView
731 return CGRectInset([[[aView window] platformWindow] contentBounds], 5.0, 5.0);
733 return CGRectInset([[[aView window] screen] visibleFrame], 5.0, 5.0);
736 - (void)popUpMenuPositioningItem:(
CPMenuItem)anItem atLocation:(CGPoint)aLocation inView:(
CPView)aView callback:(Function)aCallback
738 [
self _popUpMenuPositioningItem:anItem
746 - (void)_popUpMenuPositioningItem:(
CPMenuItem)anItem atLocation:(CGPoint)aLocation topY:(
float)aTopY bottomY:(
float)aBottomY inView:(
CPView)aView callback:(Function)aCallback
752 itemIndex = [
self indexOfItem:anItem];
755 throw "In call to popUpMenuPositioningItem:atLocation:inView:callback:, menu item " +
756 anItem + " is not present in
menu " + self;
759 var theWindow = [aView window];
761 if (aView && !theWindow)
762 throw "In call to popUpMenuPositioningItem:atLocation:inView:callback:, view is not in any window.";
764 [
self _menuWillOpen];
768 aLocation = [theWindow convertBaseToGlobal:[aView convertPoint:aLocation toView:nil]];
771 var menuWindow = [_CPMenuWindow menuWindowWithMenu:self font:[
self font]];
773 [menuWindow setBackgroundStyle:_CPMenuWindowPopUpBackgroundStyle];
778 aLocation.y -= [menuWindow deltaYForItemAtIndex:itemIndex];
781 var constraintRect = [
CPMenu _constraintRectForView:aView];
783 [menuWindow setFrameOrigin:aLocation];
784 [menuWindow setConstraintRect:constraintRect];
787 if (![menuWindow hasMinimumNumberOfVisibleItems])
789 var unconstrainedFrame = [menuWindow unconstrainedFrame],
790 unconstrainedY = CGRectGetMinY(unconstrainedFrame);
793 if (unconstrainedY >= CGRectGetMaxY(constraintRect) || [menuWindow canScrollDown])
797 aTopY = [theWindow convertBaseToGlobal:[aView convertPoint:CGPointMake(0.0, aTopY) toView:nil]].y;
799 unconstrainedFrame.origin.y = MIN(CGRectGetMaxY(constraintRect), aTopY) - CGRectGetHeight(unconstrainedFrame);
803 else if (unconstrainedY < CGRectGetMinY(constraintRect) || [menuWindow canScrollUp])
807 aBottomY = [theWindow convertBaseToGlobal:[aView convertPoint:CGPointMake(0.0, aBottomY) toView:nil]].y;
809 unconstrainedFrame.origin.y = MAX(CGRectGetMinY(constraintRect), aBottomY);
812 [menuWindow setFrameOrigin:CGRectIntersection(unconstrainedFrame, constraintRect).origin];
817 [menuWindow setPlatformWindow:[[aView window] platformWindow]];
819 [menuWindow orderFront:self];
822 [[_CPMenuManager sharedMenuManager]
823 beginTracking:[CPApp currentEvent]
824 menuContainer:menuWindow
825 constraintRect:constraintRect
829 + (Function)trackingCallbackWithCallback:(Function)aCallback
831 return function(aMenuWindow, aMenu)
833 [aMenuWindow setMenu:nil];
834 [aMenuWindow orderOut:self];
836 [_CPMenuWindow poolMenuWindow:aMenuWindow];
841 [aMenu _performActionOfHighlightedItemChain];
853 [[_CPMenuManager sharedMenuManager] cancelActiveMenu];
855 [aMenu _menuWillOpen];
860 var theWindow = [aView window],
861 menuWindow = [_CPMenuWindow menuWindowWithMenu:aMenu font:aFont];
863 [menuWindow setBackgroundStyle:_CPMenuWindowPopUpBackgroundStyle];
865 var constraintRect = [
CPMenu _constraintRectForView:aView],
866 aLocation = [[anEvent window] convertBaseToGlobal:[anEvent locationInWindow]];
868 [menuWindow setConstraintRect:constraintRect];
869 [menuWindow setFrameOrigin:aLocation];
872 if (![menuWindow hasMinimumNumberOfVisibleItems])
874 var unconstrainedFrame = [menuWindow unconstrainedFrame],
875 unconstrainedY = CGRectGetMinY(unconstrainedFrame);
878 if (unconstrainedY >= CGRectGetMaxY(constraintRect) || [menuWindow canScrollDown])
879 unconstrainedFrame.origin.y = MIN(CGRectGetMaxY(constraintRect), aLocation.y) - CGRectGetHeight(unconstrainedFrame);
882 else if (unconstrainedY < CGRectGetMinY(constraintRect) || [menuWindow canScrollUp])
883 unconstrainedFrame.origin.y = MAX(CGRectGetMinY(constraintRect), aLocation.y);
885 [menuWindow setFrameOrigin:CGRectIntersection(unconstrainedFrame, constraintRect).origin];
889 [menuWindow setPlatformWindow:[[aView window] platformWindow]];
891 [menuWindow orderFront:self];
893 [[_CPMenuManager sharedMenuManager]
894 beginTracking:anEvent
895 menuContainer:menuWindow
896 constraintRect:[
CPMenu _constraintRectForView:aView]
905 - (void)setShowsStateColumn:(BOOL)shouldShowStateColumn
907 _showsStateColumn = shouldShowStateColumn;
913 - (BOOL)showsStateColumn
915 return _showsStateColumn;
925 if (_highlightedIndex < 0)
928 var highlightedItem = _items[_highlightedIndex];
930 if ([highlightedItem isSeparatorItem])
933 return highlightedItem;
938 - (void)setDelegate:(
id)aDelegate
940 _delegate = aDelegate;
948 - (void)_menuWillOpen
950 var delegate = [
self delegate];
952 if ([delegate respondsToSelector:
@selector(menuWillOpen:)])
953 [delegate menuWillOpen:self];
956 - (void)_menuDidClose
960 _lastCloseEvent = [CPApp currentEvent];
962 var delegate = [
self delegate];
964 if ([delegate respondsToSelector:
@selector(menuDidClose:)])
965 [delegate menuDidClose:self];
972 - (void)cancelTracking
977 - (void)_fireCancelTrackingEvent
980 otherEventWithType:CPAppKitDefined
981 location:CGPointMakeZero()
995 [_CPDisplayServer run];
999 - (void)_setMenuWindow:(_CPMenuWindow)aMenuWindow
1001 _menuWindow = aMenuWindow;
1022 if (_autoenablesItems)
1026 count = _items.length,
1027 characters = [anEvent charactersIgnoringModifiers],
1028 modifierFlags = [anEvent modifierFlags];
1030 for (; index < count; ++index)
1032 var item = _items[index];
1034 if ([anEvent _triggersKeyEquivalent:[item keyEquivalent] withModifierMask:[item keyEquivalentModifierMask]])
1036 if ([item isEnabled])
1046 if ([[item submenu] performKeyEquivalent:anEvent])
1058 - (void)performActionForItemAtIndex:(CPUInteger)anIndex
1060 var item = _items[anIndex];
1062 [CPApp sendAction:[item action] to:[item target] from:item];
1069 - (void)_highlightItemAtIndex:(
int)anIndex
1071 if (_highlightedIndex === anIndex)
1075 [[_items[_highlightedIndex] _menuItemView] highlight:NO];
1077 _highlightedIndex = anIndex;
1080 [[_items[_highlightedIndex] _menuItemView] highlight:YES];
1082 if (_highlightedIndex !==
CPNotFound && _menuWindow)
1083 [_menuWindow._menuView scrollRectToVisible:[[_items[_highlightedIndex] _menuItemView] frame]];
1086 - (void)_setMenuName:(
CPString)aName
1088 if (_name === aName)
1093 if (_name ===
@"CPMainMenu")
1094 [CPApp setMainMenu:self];
1102 - (void)awakeFromCib
1104 if (_name ===
@"_CPMainMenu")
1106 [
self _setMenuName:@"CPMainMenu"];
1111 - (void)_menuWithName:(
CPString)aName
1113 if (aName === _name)
1116 for (var i = 0, count = [_items count]; i < count; i++)
1118 var
menu = [[_items[i] submenu] _menuWithName:aName];
1131 - (CPUInteger)countOfItems
1133 return [_items count];
1138 return [_items objectAtIndex:anIndex];
1143 return [_items objectsAtIndexes:indexes];
1150 - (void)insertObject:(
CPMenuItem)aMenuItem inItemsAtIndex:(CPUInteger)anIndex
1152 var
menu = [aMenuItem menu];
1160 [aMenuItem setMenu:self];
1161 [
self _highlightItemAtIndex:CPNotFound];
1162 [_items insertObject:aMenuItem atIndex:anIndex];
1167 userInfo:@{ @"CPMenuItemIndex": anIndex }];
1170 - (void)removeObjectFromItemsAtIndex:(CPUInteger)anIndex
1172 if (anIndex < 0 || anIndex >= [_items count])
1175 [[_items objectAtIndex:anIndex] setMenu:nil];
1176 [
self _highlightItemAtIndex:CPNotFound];
1177 [_items removeObjectAtIndex:anIndex];
1182 userInfo:@{ @"CPMenuItemIndex": anIndex }];
1202 self = [
super init];
1206 _title = [aCoder decodeObjectForKey:CPMenuTitleKey];
1207 _items = [aCoder decodeObjectForKey:CPMenuItemsKey];
1209 [
self _setMenuName:[aCoder decodeObjectForKey:CPMenuNameKey]];
1211 _showsStateColumn = ![aCoder containsValueForKey:CPMenuShowsStateColumnKey] || [aCoder decodeBoolForKey:CPMenuShowsStateColumnKey];
1213 _autoenablesItems = ![aCoder containsValueForKey:CPMenuAutoEnablesItemsKey] || [aCoder decodeBoolForKey:CPMenuAutoEnablesItemsKey];
1227 [aCoder encodeObject:_title forKey:CPMenuTitleKey];
1232 [aCoder encodeObject:_items forKey:CPMenuItemsKey];
1234 if (!_showsStateColumn)
1237 if (!_autoenablesItems)