44 CPMenu _searchMenuTemplate;
48 CPArray _recentSearches;
51 BOOL _sendsWholeSearchString;
52 BOOL _sendsSearchStringImmediately;
53 BOOL _canResignFirstResponder;
69 @"image-search-inset" : CGInsetMake(0, 0, 0, 5),
70 @"image-cancel-inset" : CGInsetMake(0, 5, 0, 0),
71 @"search-menu-offset": CGPointMake(10, -4),
72 @"search-right-margin": 2
76 + (Class)_binderClassForBinding:(
CPString)aBinding
78 if (aBinding === CPPredicateBinding)
79 return [_CPSearchFieldPredicateBinder class];
81 return [
super _binderClassForBinding:aBinding];
84 - (id)initWithFrame:(CGRect)frame
86 if (
self = [super initWithFrame:
frame])
89 _sendsWholeSearchString = NO;
90 _sendsSearchStringImmediately = NO;
91 _recentsAutosaveName = nil;
101 _recentSearches = [CPArray array];
103 [
self setBezeled:YES];
104 [
self setBezelStyle:CPTextFieldRoundedBezel];
105 [
self setBordered:YES];
106 [
self setEditable:YES];
107 [
self setContinuous:YES];
109 var
bounds = [
self bounds],
110 cancelButton = [[
CPButton alloc] initWithFrame:[
self cancelButtonRectForBounds:bounds]],
111 searchButton = [[
CPButton alloc] initWithFrame:[
self searchButtonRectForBounds:bounds]];
113 [
self setCancelButton:cancelButton];
114 [
self resetCancelButton];
116 [
self setSearchButton:searchButton];
117 [
self resetSearchButton];
119 _canResignFirstResponder = YES;
124 #pragma mark Override observers 126 - (void)_removeObservers
131 [
super _removeObservers];
136 - (void)_addObservers
141 [
super _addObservers];
153 if (button != _searchButton)
155 [_searchButton removeFromSuperview];
156 _searchButton = button;
159 [_searchButton setAutoresizingMask:CPViewMaxXMargin];
170 return _searchButton;
177 - (void)resetSearchButton
180 searchButtonImage = (_searchMenuTemplate === nil) ? [
self currentValueForThemeAttribute:
@"image-search"] : [
self currentValueForThemeAttribute:
@"image-find"];
184 [button
setImage:searchButtonImage];
194 if (button != _cancelButton)
196 [_cancelButton removeFromSuperview];
197 _cancelButton = button;
200 [_cancelButton setAutoresizingMask:CPViewMinXMargin];
201 [_cancelButton setTarget:self];
202 [_cancelButton setAction:@selector(cancelOperation:)];
203 [_cancelButton setButtonType:CPMomentaryChangeButton];
204 [
self _updateCancelButtonVisibility];
215 return _cancelButton;
222 - (void)resetCancelButton
227 [button
setImage:[
self valueForThemeAttribute:@"image-cancel"]];
228 [button
setAlternateImage:[
self valueForThemeAttribute:@"image-cancel-pressed"]];
241 - (CGRect)searchTextRectForBounds:(CGRect)rect
244 width = CGRectGetWidth(rect),
250 rightMargin = [
self currentValueForThemeAttribute:@"search-right-margin"];
251 leftOffset = CGRectGetMaxX(searchBounds) + rightMargin;
257 width = CGRectGetMinX(cancelRect) - leftOffset;
260 return CGRectMake(leftOffset, CGRectGetMinY(rect),
width, CGRectGetHeight(rect));
268 - (CGRect)searchButtonRectForBounds:(CGRect)rect
270 var size = [[
self currentValueForThemeAttribute:@"image-search"] size] || CGSizeMakeZero(),
271 inset = [
self currentValueForThemeAttribute:@"image-search-inset"];
273 return CGRectMake(inset.left - inset.right, inset.top - inset.bottom + (CGRectGetHeight(rect) - size.height) / 2, size.width, size.height);
281 - (CGRect)cancelButtonRectForBounds:(CGRect)rect
283 var size = [[
self currentValueForThemeAttribute:@"image-cancel"] size] || CGSizeMakeZero(),
284 inset = [
self currentValueForThemeAttribute:@"image-cancel-inset"];
286 return CGRectMake(CGRectGetWidth(rect) - size.width + inset.left - inset.right, inset.top - inset.bottom + (CGRectGetHeight(rect) - size.width) / 2, size.height, size.height);
296 return _searchMenuTemplate;
304 - (void)setSearchMenuTemplate:(
CPMenu)aMenu
306 _searchMenuTemplate = aMenu;
309 [
self _loadRecentSearchList];
310 [
self _updateSearchMenu];
318 - (BOOL)sendsWholeSearchString
320 return _sendsWholeSearchString;
327 - (void)setSendsWholeSearchString:(BOOL)flag
329 _sendsWholeSearchString = flag;
336 - (BOOL)sendsSearchStringImmediately
338 return _sendsSearchStringImmediately;
345 - (void)setSendsSearchStringImmediately:(BOOL)flag
347 _sendsSearchStringImmediately = flag;
355 - (int)maximumRecents
357 return _maximumRecents;
364 - (void)setMaximumRecents:(
int)max
371 _maximumRecents = max;
378 - (CPArray)recentSearches
380 return _recentSearches;
388 - (void)setRecentSearches:(CPArray)searches
390 var max = MIN([
self maximumRecents], [searches count]);
392 searches = [searches subarrayWithRange:CPMakeRange(0, max)];
393 _recentSearches = searches;
394 [
self _autosaveRecentSearchList];
403 return _recentsAutosaveName;
412 if (_recentsAutosaveName != nil)
413 [
self _deregisterForAutosaveNotification];
415 _recentsAutosaveName =
name;
417 if (_recentsAutosaveName != nil)
418 [
self _registerForAutosaveNotification];
423 - (CGRect)contentRectForBounds:(CGRect)bounds
429 + (double)_keyboardDelayForPartialSearchString:(
CPString)string
431 return (6 - MIN([
string length], 4)) / 10;
441 return [
super isOpaque] && [_cancelButton isOpaque] && [_searchButton isOpaque];
444 - (void)_updateCancelButtonVisibility
446 [_cancelButton setHidden:([[
self stringValue] length] === 0)];
451 if (![
self sendsWholeSearchString])
453 if ([
self sendsSearchStringImmediately])
454 [
self _sendPartialString];
457 [_partialStringTimer invalidate];
458 var timeInterval = [
CPSearchField _keyboardDelayForPartialSearchString:[
self stringValue]];
462 selector:@selector(_sendPartialString)
468 [
self _updateCancelButtonVisibility];
471 - (void)_sendAction:(
id)sender
473 [
self sendAction:[
self action] to:[
self target]];
476 - (BOOL)sendAction:(
SEL)anAction to:(
id)anObject
480 [_partialStringTimer invalidate];
482 [
self _addStringToRecentSearches:[
self stringValue]];
483 [
self _updateCancelButtonVisibility];
486 - (void)_addStringToRecentSearches:(
CPString)string
488 if (
string === nil ||
string ===
@"" || [_recentSearches containsObject:
string])
492 [searches addObject:string];
493 [
self setRecentSearches:searches];
494 [
self _updateSearchMenu];
500 if (CGRectContainsPoint([
self frame], aPoint))
506 - (BOOL)resignFirstResponder
516 if (CGRectContainsPoint([
self searchButtonRectForBounds:[
self bounds]], point))
518 if (_searchMenuTemplate == nil)
520 if ([_searchButton target] && [_searchButton action])
521 [_searchButton mouseDown:anEvent];
523 [
self _sendAction:self];
528 else if (CGRectContainsPoint([
self cancelButtonRectForBounds:[
self bounds]], point))
529 [_cancelButton mouseDown:anEvent];
572 [item setTag:CPSearchFieldClearRecentsMenuItemTag];
573 [item setTarget:self];
574 [template addItem:item];
579 [item setTag:CPSearchFieldRecentsTitleMenuItemTag];
580 [item setEnabled:NO];
581 [template addItem:item];
586 [item setTag:CPSearchFieldRecentsMenuItemTag];
587 [item setTarget:self];
588 [template addItem:item];
593 - (void)_updateSearchMenu
595 if (_searchMenuTemplate === nil)
599 countOfRecents = [_recentSearches count],
600 numberOfItems = [_searchMenuTemplate numberOfItems];
602 [menu setAutoenablesItems:NO];
604 for (var i = 0; i < numberOfItems; i++)
606 var item = [[_searchMenuTemplate itemAtIndex:i] copy];
611 if (countOfRecents === 0)
614 if ([
menu numberOfItems] > 0)
615 [
self _addSeparatorToMenu:
menu];
620 var itemAction =
@selector(_searchFieldSearch:);
622 for (var recentIndex = 0; recentIndex < countOfRecents; ++recentIndex)
627 [item setTarget:self];
628 [menu addItem:recentItem];
635 if (countOfRecents === 0)
638 if ([
menu numberOfItems] > 0)
639 [
self _addSeparatorToMenu:
menu];
641 [item setAction:@selector(_searchFieldClearRecents:)];
642 [item setTarget:self];
646 if (countOfRecents !== 0)
649 if ([
menu numberOfItems] > 0)
650 [
self _addSeparatorToMenu:
menu];
654 [item setEnabled:([item isEnabled] && [item action] != nil && [item target] != nil)];
658 [menu setDelegate:self];
663 - (void)_addSeparatorToMenu:(
CPMenu)aMenu
666 [separator setEnabled:NO];
672 _canResignFirstResponder = NO;
677 _canResignFirstResponder = YES;
684 if (_searchMenu === nil || [_searchMenu numberOfItems] === 0 || ![
self isEnabled] || ([_recentSearches count] === 0))
687 var aFrame = [[
self superview] convertRect:[
self frame] toView:nil],
688 offset = [
self currentValueForThemeAttribute:@"search-menu-offset"],
689 location = CGPointMake(aFrame.origin.x + offset.x, aFrame.origin.y + aFrame.size.height + offset.y);
693 [
self selectAll:nil];
697 - (void)_sendPartialString
699 [
super sendAction:[
self action] to:[
self target]];
700 [_partialStringTimer invalidate];
703 - (void)cancelOperation:(
id)sender
708 [
self _updateCancelButtonVisibility];
711 - (void)_searchFieldSearch:(
id)sender
713 var searchString = [sender title];
716 [
self _addStringToRecentSearches:searchString];
718 [
self setObjectValue:searchString];
719 [
self _sendPartialString];
720 [
self selectAll:nil];
722 [
self _updateCancelButtonVisibility];
725 - (void)_searchFieldClearRecents:(
id)sender
727 [
self setRecentSearches:[CPArray array]];
728 [
self _updateSearchMenu];
729 [
self setStringValue:@""];
730 [
self _updateCancelButtonVisibility];
733 - (void)_registerForAutosaveNotification
738 - (void)_deregisterForAutosaveNotification
743 - (void)_autosaveRecentSearchList
745 if (_recentsAutosaveName != nil)
749 - (void)_updateAutosavedRecents:(
id)notification
751 var
name = [notification object];
752 [[
CPUserDefaults standardUserDefaults] setObject:_recentSearches forKey:name];
755 - (void)_loadRecentSearchList
757 var
name = [
self recentsAutosaveName];
761 var list = [[
CPUserDefaults standardUserDefaults] objectForKey:name];
764 _recentSearches = list;
769 [
super unbind:aBinding];
773 if (aBinding === CPPredicateBinding)
774 [[[
self class] _binderClassForBinding:aBinding] unbind:CPValueBinding forObject:self];
787 - (void)updateTrackingAreas
789 if (_searchButtonTrackingArea)
792 _searchButtonTrackingArea = nil;
795 if (_cancelButtonTrackingArea)
798 _cancelButtonTrackingArea = nil;
804 options:CPTrackingCursorUpdate | CPTrackingActiveInKeyWindow
814 options:CPTrackingCursorUpdate | CPTrackingActiveInKeyWindow
826 if ([[[anEvent trackingArea] userInfo] objectForKey:
@"isButton"])
846 [_searchButton removeFromSuperview];
847 [_cancelButton removeFromSuperview];
856 [coder encodeBool:_sendsWholeSearchString forKey:CPSendsWholeSearchStringKey];
857 [coder encodeBool:_sendsSearchStringImmediately forKey:CPSendsSearchStringImmediatelyKey];
858 [coder encodeInt:_maximumRecents forKey:CPMaximumRecentsKey];
860 if (_recentsAutosaveName)
861 [coder encodeObject:_recentsAutosaveName forKey:CPRecentsAutosaveNameKey];
863 if (_searchMenuTemplate)
864 [coder encodeObject:_searchMenuTemplate forKey:CPSearchMenuTemplateKey];
869 if (
self = [super initWithCoder:coder])
872 _sendsWholeSearchString = [coder decodeBoolForKey:CPSendsWholeSearchStringKey];
873 _sendsSearchStringImmediately = [coder decodeBoolForKey:CPSendsSearchStringImmediatelyKey];
874 _maximumRecents = [coder decodeIntForKey:CPMaximumRecentsKey];
876 var
template = [coder decodeObjectForKey:CPSearchMenuTemplateKey];
889 @implementation _CPSearchFieldPredicateBinder :
CPBinder 895 - (void)setValue:(
id)aValue forBinding:(
CPString)aBinding
897 if (aBinding === CPPredicateBinding)
899 var options = [_info objectForKey:CPOptionsKey];
901 _controller = [_info objectForKey:CPObservedObjectKey];
902 _predicateFormat = [options objectForKey:"CPPredicateFormat"];
903 [_source bind:CPValueBinding toObject:self withKeyPath:"searchFieldValue" options:nil];
907 - (void)setSearchFieldValue:(
CPString)aValue
909 var destination = [_info objectForKey:CPObservedObjectKey],
910 keyPath = [_info objectForKey:CPObservedKeyPathKey];
912 var formatString = _predicateFormat.replace(/\$value/g,
"%@");
913 [
self suppressSpecificNotificationFromObject:destination keyPath:keyPath];
916 [_controller setFilterPredicate:[CPPredicate predicateWithFormat:formatString, aValue]];
918 [_controller setFilterPredicate:nil];
920 [
self unsuppressSpecificNotificationFromObject:destination keyPath:keyPath];
924 return [_source stringValue];
CPSearchFieldClearRecentsMenuItemTag
id mouseEventWithType:location:modifierFlags:timestamp:windowNumber:context:eventNumber:clickCount:pressure:(CPEventType anEventType, [location] CGPoint aPoint, [modifierFlags] unsigned modifierFlags, [timestamp] CPTimeInterval aTimestamp, [windowNumber] int aWindowNumber, [context] CPGraphicsContext aGraphicsContext, [eventNumber] int anEventNumber, [clickCount] int aClickCount, [pressure] float aPressure)
void addSubview:(CPView aSubview)
void encodeWithCoder:(CPCoder aCoder)
void setAction:(SEL anAction)
var CPSendsSearchStringImmediatelyKey
void textDidChange:(CPNotification note)
An object representation of nil.
void setTarget:(id aTarget)
BOOL becomeFirstResponder()
void addObserver:selector:name:object:(id anObserver, [selector] SEL aSelector, [name] CPString aNotificationName, [object] id anObject)
CGPoint locationInWindow()
void postNotificationName:object:(CPString aNotificationName, [object] id anObject)
void setImageScaling:(CPImageScaling scaling)
CPNotificationCenter defaultCenter()
A mutable key-value pair collection.
An immutable string (collection of characters).
CGPoint convertPoint:fromView:(CGPoint aPoint, [fromView] CPView aView)
var CPAutosavedRecentsChangedNotification
global CPApp CPSearchFieldRecentsTitleMenuItemTag
BOOL sendAction:to:(SEL anAction, [to] id anObject)
CGRect contentRectForBounds:(CGRect bounds)
CGRect searchButtonRectForBounds:(CGRect rect)
void setAutoresizingMask:(unsigned aMask)
CGRect searchTextRectForBounds:(CGRect rect)
A notification that can be posted to a CPNotificationCenter.
CPTimer scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:(CPTimeInterval seconds, [target] id aTarget, [selector] SEL aSelector, [userInfo] id userInfo, [repeats] BOOL shouldRepeat)
void setObjectValue:(id aValue)
void setSearchMenuTemplate:(CPMenu aMenu)
var CPSearchMenuTemplateKey
A timer object that can send a message after the given time interval.
Defines methods for use when archiving & restoring (enc/decoding).
var CPSendsWholeSearchStringKey
Sends messages (CPNotification) between objects.
var CPRecentsAutosaveNameKey
CPNotification notificationWithName:object:userInfo:(CPString aNotificationName, [object] id anObject, [userInfo] CPDictionary aUserInfo)
void addTrackingArea:(CPTrackingArea trackingArea)
void setRecentsAutosaveName:(CPString name)
BOOL resignFirstResponder()
void mouseDown:(CPEvent anEvent)
void updateTrackingAreas()
void removeObserver:name:object:(id anObserver, [name] CPString aNotificationName, [object] id anObject)
CPTrackingArea initWithRect:options:owner:userInfo:(CGRect aRect, [options] CPTrackingAreaOptions options, [owner] id owner, [userInfo] CPDictionary userInfo)
CPSearchFieldRecentsMenuItemTag
CGRect cancelButtonRectForBounds:(CGRect rect)
CPSearchFieldNoRecentsMenuItemTag
void removeTrackingArea:(CPTrackingArea trackingArea)
void cursorUpdate:(CPEvent anEvent)