33 CPUInteger _selectedIndex;
34 CPRectEdge _preferredEdge;
39 return "popup-button";
42 + (CPSet)keyPathsForValuesAffectingSelectedIndex
44 return [CPSet setWithObject:@"objectValue"];
47 + (CPSet)keyPathsForValuesAffectingSelectedTag
49 return [CPSet setWithObject:@"objectValue"];
52 + (CPSet)keyPathsForValuesAffectingSelectedItem
54 return [CPSet setWithObject:@"objectValue"];
63 - (id)initWithFrame:(CGRect)aFrame pullsDown:(BOOL)shouldPullDown
82 [
self addObserver:self forKeyPath:@"menu.items" options:options context:nil];
83 [
self addObserver:self forKeyPath:@"_firstItem.changeCount" options:options context:nil];
84 [
self addObserver:self forKeyPath:@"selectedItem.changeCount" options:options context:nil];
90 - (id)initWithFrame:(CGRect)aFrame
105 - (void)setPullsDown:(BOOL)shouldPullDown
108 var changed = [
self setThemeState:CPPopUpButtonStatePullsDown];
115 var items = [[
self menu] itemArray];
117 if ([items count] <= 0)
130 return [
self hasThemeState:CPPopUpButtonStatePullsDown];
140 [[
self menu] addItem:anItem];
149 [[
self menu] addItemWithTitle:aTitle action:NULL keyEquivalent:nil];
156 - (void)addItemsWithTitles:(CPArray)titles
159 count = [titles count];
161 for (; index < count; ++index)
162 [
self addItemWithTitle:titles[index]];
170 - (void)insertItemWithTitle:(
CPString)aTitle atIndex:(
int)anIndex
173 count = [items count];
176 if ([items[count] title] == aTitle)
179 [[
self menu] insertItemWithTitle:aTitle action:NULL keyEquivalent:nil atIndex:anIndex];
185 - (void)removeAllItems
187 [[
self menu] removeAllItems];
205 - (void)removeItemAtIndex:(
int)anIndex
207 [[
self menu] removeItemAtIndex:anIndex];
219 if (indexOfSelectedItem < 0 || indexOfSelectedItem > [
self numberOfItems] - 1)
222 return [[
self menu] itemAtIndex:indexOfSelectedItem];
236 - (int)indexOfSelectedItem
238 return _selectedIndex;
255 - (void)selectItemAtIndex:(CPUInteger)anIndex
260 - (void)setSelectedIndex:(CPUInteger)anIndex
265 - (CPUInteger)selectedIndex
274 - (void)setObjectValue:(
id)anIndex
278 anIndex = parseInt(+anIndex, 10);
280 if (indexOfSelectedItem === anIndex)
283 if (indexOfSelectedItem >= 0 && ![
self pullsDown])
286 _selectedIndex = anIndex;
288 if (indexOfSelectedItem >= 0 && ![
self pullsDown])
296 return _selectedIndex;
303 - (void)selectItemWithTag:(
int)aTag
324 return [[
self menu] numberOfItems];
332 return [[
self menu] itemArray];
341 return [[
self menu] itemAtIndex:anIndex];
350 return [[[
self menu] itemAtIndex:anIndex] title];
356 - (CPArray)itemTitles
358 return [[
self itemArray] arrayByApplyingBlock:function(item)
370 var
menu = [
self menu],
371 itemIndex = [menu indexOfItemWithTitle:aTitle];
376 return [menu itemAtIndex:itemIndex];
384 return [[[
self menu] itemArray] lastObject];
394 return [[
self menu] indexOfItem:aMenuItem];
401 - (int)indexOfItemWithTag:(
int)aTag
403 return [[
self menu] indexOfItemWithTag:aTag];
412 return [[
self menu] indexOfItemWithTitle:aTitle];
421 - (int)indexOfItemWithRepresentedObject:(
id)anObject
423 return [[
self menu] indexOfItemWithRepresentedObject:anObject];
433 - (int)indexOfItemWithTarget:(
id)aTarget action:(
SEL)anAction
435 return [[
self menu] indexOfItemWithTarget:aTarget action:anAction];
444 - (CPRectEdge)preferredEdge
446 return _preferredEdge;
454 - (void)setPreferredEdge:(CPRectEdge)aRectEdge
456 _preferredEdge = aRectEdge;
466 if ([
self title] === aTitle)
469 if ([
self pullsDown])
471 var items = [[
self menu] itemArray];
473 if ([items count] <= 0)
478 [items[0] setTitle:aTitle];
521 - (void)synchronizeTitleAndSelectedItem
525 if ([
self pullsDown])
527 var items = [[
self menu] itemArray];
529 if ([items count] > 0)
539 - (void)observeValueForKeyPath:(
CPString)aKeyPath ofObject:(
id)anObject change:(
CPDictionary)changes context:(
id)aContext
543 if (!pullsDown && aKeyPath ===
@"selectedItem.changeCount" ||
544 pullsDown && (aKeyPath ===
@"_firstItem" || aKeyPath ===
@"_firstItem.changeCount"))
548 if (aKeyPath ===
@"menu")
550 aKeyPath =
@"menu.items";
552 [changes
setObject:CPKeyValueChangeSetting
forKey:CPKeyValueChangeKindKey];
553 [changes
setObject:[[
self menu] itemArray]
forKey:CPKeyValueChangeNewKey];
556 if (aKeyPath ===
@"menu.items")
558 var changeKind = [changes
objectForKey:CPKeyValueChangeKindKey],
564 indexes = [changes
objectForKey:CPKeyValueChangeIndexesKey];
566 if ([indexes containsIndex:0] && [
self pullsDown])
567 [
self _firstItemDidChange];
569 if (![
self pullsDown] && [indexes containsIndex:indexOfSelectedItem])
572 indexOfSelectedItem = 0;
577 while ((index = [indexes indexGreaterThanIndex:index]) !==
CPNotFound &&
578 index <= indexOfSelectedItem)
579 --indexOfSelectedItem;
587 var indexes = [changes
objectForKey:CPKeyValueChangeIndexesKey];
589 if (pullsDown && [indexes containsIndex:0] ||
590 !pullsDown && [indexes containsIndex:indexOfSelectedItem])
597 var newItems = [changes
objectForKey:CPKeyValueChangeNewKey];
599 [newItems enumerateObjectsUsingBlock:function(aMenuItem)
601 var action = [aMenuItem
action];
606 if (action === @selector(_popUpItemAction:))
612 [
self _firstItemDidChange];
620 var indexes = [changes
objectForKey:CPKeyValueChangeIndexesKey];
622 if ([
self pullsDown] && [indexes containsIndex:0])
624 [
self _firstItemDidChange];
626 if ([
self numberOfItems] > 1)
631 while ((index = [indexes indexGreaterThanIndex:index]) !==
CPNotFound &&
632 index <= originalIndex)
639 if (indexOfSelectedItem < 0)
647 while ((index = [indexes indexGreaterThanIndex:index]) !==
CPNotFound &&
648 index <= indexOfSelectedItem)
649 ++indexOfSelectedItem;
662 if (![
self isEnabled] || ![
self numberOfItems])
665 var
menu = [
self menu];
668 if (
menu._lastCloseEvent === anEvent)
674 minimumWidth = CGRectGetWidth(
bounds);
677 [menu setFont:[
self font]];
679 if ([
self pullsDown])
681 var positionedItem = nil,
682 location = CGPointMake(0.0, CGRectGetMaxY(
bounds) - 1);
688 standardLeftMargin = [_CPMenuWindow _standardLeftMargin] + [_CPMenuItemStandardView _standardLeftMargin],
689 location = CGPointMake(CGRectGetMinX(contentRect) - standardLeftMargin, 0.0);
691 minimumWidth += standardLeftMargin;
694 [menu _highlightItemAtIndex:CPNotFound];
697 [menu setMinimumWidth:minimumWidth];
700 _popUpMenuPositioningItem:positionedItem
702 topY:CGRectGetMinY(bounds)
703 bottomY:CGRectGetMaxY(bounds)
705 callback:function(aMenu)
709 var highlightedItem = [aMenu highlightedItem];
711 if ([highlightedItem _isSelectable])
737 - (void)_popUpItemAction:(
id)aSender
739 [
self sendAction:[
self action] to:[
self target]];
742 - (void)_firstItemDidChange
744 [
self willChangeValueForKey:@"_firstItem"];
745 [
self didChangeValueForKey:@"_firstItem"];
747 [[
self _firstItem] setHidden:YES];
752 if ([
self numberOfItems] <= 0)
755 return [[
self menu] itemAtIndex:0];
758 - (void)takeValueFromKeyPath:(
CPString)aKeyPath ofObjects:(CPArray)objects
760 var count = objects.
length,
761 value = [objects[0] valueForKeyPath:aKeyPath];
767 if (value !== [objects[count] valueForKeyPath:aKeyPath])
775 + (Class)_binderClassForBinding:(
CPString)aBinding
777 if (aBinding == CPSelectedIndexBinding ||
778 aBinding == CPSelectedObjectBinding ||
779 aBinding == CPSelectedTagBinding ||
780 aBinding == CPSelectedValueBinding ||
781 aBinding == CPContentBinding ||
782 aBinding == CPContentObjectsBinding ||
783 aBinding == CPContentValuesBinding)
785 var capitalizedBinding = aBinding.charAt(0).toUpperCase() + aBinding.substr(1);
787 return [CPClassFromString(@"_CPPopUpButton" + capitalizedBinding + "Binder") class];
790 return [
super _binderClassForBinding:aBinding];
795 return (aBinding == CPSelectedIndexBinding ||
796 aBinding == CPSelectedTagBinding ||
797 aBinding == CPSelectedValueBinding);
800 - (void)_reverseSetBinding
802 [_CPPopUpButtonSelectionBinder _reverseSetValueFromExclusiveBinderForObject:self];
804 [
super _reverseSetBinding];
808 @implementation _CPPopUpButtonContentBinder :
CPBinder 813 - (CPInteger)_getInsertNullOffset
815 var options = [_info objectForKey:CPOptionsKey];
817 return [options objectForKey:CPInsertsNullPlaceholderBindingOption] ? 1 : 0;
822 var options = [_info objectForKey:CPOptionsKey],
823 placeholder = [options objectForKey:CPNullPlaceholderBindingOption] ||
@"";
825 if (placeholder === [
CPNull null])
831 - (id)transformValue:(CPArray)contentArray withOptions:(
CPDictionary)options
837 - (void)setValue:(CPArray)contentArray forBinding:(
CPString)aBinding
839 [
self _setContent:contentArray];
840 [
self _setContentValuesIfNeeded:contentArray];
843 - (id)valueForBinding:(
CPString)aBinding
845 return [
self _content];
848 - (void)_setContent:(CPArray)aValue
850 var count = [aValue count],
851 options = [_info objectForKey:CPOptionsKey],
852 offset = [
self _getInsertNullOffset],
853 selectedBindingInfo = [_source infoForBinding:CPSelectedObjectBinding],
854 selectedObject = nil;
856 if (selectedBindingInfo)
858 var destination = [selectedBindingInfo objectForKey:CPObservedObjectKey],
859 keyPath = [selectedBindingInfo objectForKey:CPObservedKeyPathKey];
861 selectedObject = [destination valueForKeyPath:keyPath];
864 if (count + offset != [_source numberOfItems])
866 [_source removeAllItems];
869 [_source addItemWithTitle:[
self _getNullPlaceholder]];
871 for (var i = 0; i < count; i++)
874 itemValue = [aValue objectAtIndex:i];
876 [
self _setValue:itemValue forItem:item withOptions:options];
877 [_source addItem:item];
882 if (itemValue === selectedObject)
884 [_source setSelectedIndex:[_source numberOfItems] - 1];
890 for (var i = 0; i < count; i++)
892 [
self _setValue:[aValue objectAtIndex:i] forItem:[_source itemAtIndex:i + offset] withOptions:options];
897 - (void)_setContentValuesIfNeeded:(CPArray)values
899 var offset = [
self _getInsertNullOffset];
901 if (![_source infoForBinding:CPContentValuesBinding])
904 [[_source itemAtIndex:0] setTitle:[
self _getNullPlaceholder]];
906 var count = [values count];
908 for (var i = 0; i < count; i++)
909 [[_source itemAtIndex:i + offset] setTitle:[[values objectAtIndex:i] description]];
915 var value = [
self _transformValue:aValue withOptions:options];
919 - (id)_transformValue:(
id)aValue withOptions:(
CPDictionary)options
921 return [
super transformValue:aValue withOptions:options];
926 return [_source valueForKeyPath:@"itemArray.representedObject"];
930 @implementation _CPPopUpButtonContentValuesBinder : _CPPopUpButtonContentBinder
935 - (void)setValue:(CPArray)aValue forBinding:(
CPString)aBinding
937 [
super _setContent:aValue];
942 if (aValue === [
CPNull null])
945 var value = [
self _transformValue:aValue withOptions:options];
951 return [_source valueForKeyPath:@"itemArray.title"];
955 @implementation _CPPopUpButtonSelectionBinder :
CPBinder 960 - (void)setPlaceholderValue:(
id)aValue withMarker:(
CPString)aMarker forBinding:(
CPString)aBinding
962 [
self setValue:aValue forBinding:aBinding];
965 - (CPInteger)_getInsertNullOffset
969 return [options
objectForKey:CPInsertsNullPlaceholderBindingOption] ? 1 : 0;
973 @implementation _CPPopUpButtonSelectedIndexBinder : _CPPopUpButtonSelectionBinder
978 - (void)setValue:(
id)aValue forBinding:(
CPString)aBinding
980 [_source selectItemAtIndex:aValue + [
self _getInsertNullOffset]];
983 - (id)valueForBinding:(
CPString)aBinding
985 return [_source indexOfSelectedItem] - [self _getInsertNullOffset];
989 @implementation _CPPopUpButtonSelectedObjectBinder : _CPPopUpButtonSelectionBinder
994 - (void)setValue:(
id)aValue forBinding:(
CPString)aBinding
996 var index = [_source indexOfItemWithRepresentedObject:aValue],
997 offset = [
self _getInsertNullOffset];
1016 [_source selectItemAtIndex:index];
1019 - (id)valueForBinding:(
CPString)aBinding
1021 return [[_source selectedItem] representedObject];
1025 @implementation _CPPopUpButtonSelectedTagBinder : _CPPopUpButtonSelectionBinder
1030 - (void)setValue:(
id)aValue forBinding:(
CPString)aBinding
1032 [_source selectItemWithTag:aValue];
1035 - (id)valueForBinding:(
CPString)aBinding
1037 return [[_source selectedItem] tag];
1041 @implementation _CPPopUpButtonSelectedValueBinder : _CPPopUpButtonSelectionBinder
1046 - (void)setValue:(
id)aValue forBinding:(
CPString)aBinding
1048 [_source selectItemWithTitle:aValue];
1051 - (id)valueForBinding:(
CPString)aBinding
1053 return [_source titleOfSelectedItem];
1082 CPLog.warn(
self +
" was encoded with an older version of Cappuccino. Please nib2cib the original nib again or open and re-save in Atlas.");
1084 [
self setMenu:[aCoder decodeObjectForKey:DEPRECATED_CPPopUpButtonMenuKey]];
1085 [
self setObjectValue:[aCoder decodeObjectForKey:DEPRECATED_CPPopUpButtonSelectedIndexKey]];
1090 [
self addObserver:self forKeyPath:@"menu.items" options:options context:nil];
1091 [
self addObserver:self forKeyPath:@"_firstItem.changeCount" options:options context:nil];
1092 [
self addObserver:self forKeyPath:@"selectedItem.changeCount" options:options context:nil];
BOOL setThemeState:(ThemeState aState)
CPDictionary infoForBinding:forObject:(CPString aBinding, [forObject] id anObject)
An object representation of nil.
CPKeyValueChangeReplacement
void setEnabled:(BOOL isEnabled)
void setValue:forThemeAttribute:(id aValue, [forThemeAttribute] CPString aName)
A mutable key-value pair collection.
An immutable string (collection of characters).
id objectForKey:(id aKey)
CPKeyValueObservingOptionNew
void highlight:(BOOL shouldHighlight)
Defines methods for use when archiving & restoring (enc/decoding).
BOOL unsetThemeState:(ThemeState aState)
void setObject:forKey:(id anObject, [forKey] id aKey)
CPKeyValueObservingOptionOld