46 CPMenu _searchMenuTemplate;
50 CPArray _recentSearches;
53 BOOL _sendsWholeSearchString;
54 BOOL _sendsSearchStringImmediately;
55 BOOL _canResignFirstResponder;
71 @"image-search-inset" : CGInsetMake(0, 0, 0, 5),
72 @"image-cancel-inset" : CGInsetMake(0, 5, 0, 0)
76 - (id)initWithFrame:(CGRect)frame
78 if (
self = [super initWithFrame:
frame])
81 _sendsWholeSearchString = NO;
82 _sendsSearchStringImmediately = NO;
83 _recentsAutosaveName = nil;
87 _cancelButton._DOMElement.style.cursor =
"default";
88 _searchButton._DOMElement.style.cursor =
"default";
97 _recentSearches = [CPArray array];
99 [
self setBezeled:YES];
100 [
self setBezelStyle:CPTextFieldRoundedBezel];
101 [
self setBordered:YES];
102 [
self setEditable:YES];
103 [
self setContinuous:YES];
105 var
bounds = [
self bounds],
106 cancelButton = [[
CPButton alloc] initWithFrame:[
self cancelButtonRectForBounds:bounds]],
107 searchButton = [[
CPButton alloc] initWithFrame:[
self searchButtonRectForBounds:bounds]];
109 [
self setCancelButton:cancelButton];
110 [
self resetCancelButton];
112 [
self setSearchButton:searchButton];
113 [
self resetSearchButton];
115 _canResignFirstResponder = YES;
120 #pragma mark Override observers
122 - (void)_removeObservers
127 [
super _removeObservers];
132 - (void)_addObservers
137 [
super _addObservers];
149 if (button != _searchButton)
151 [_searchButton removeFromSuperview];
152 _searchButton = button;
155 [_searchButton setAutoresizingMask:CPViewMaxXMargin];
166 return _searchButton;
173 - (void)resetSearchButton
176 searchButtonImage = (_searchMenuTemplate === nil) ? [
self valueForThemeAttribute:
@"image-search"] : [
self valueForThemeAttribute:
@"image-find"];
180 [button
setImage:searchButtonImage];
190 if (button != _cancelButton)
192 [_cancelButton removeFromSuperview];
193 _cancelButton = button;
196 [_cancelButton setAutoresizingMask:CPViewMinXMargin];
197 [_cancelButton setTarget:self];
198 [_cancelButton setAction:@selector(cancelOperation:)];
199 [_cancelButton setButtonType:CPMomentaryChangeButton];
200 [
self _updateCancelButtonVisibility];
211 return _cancelButton;
218 - (void)resetCancelButton
237 - (CGRect)searchTextRectForBounds:(CGRect)rect
240 width = CGRectGetWidth(rect),
246 leftOffset = CGRectGetMaxX(searchBounds) + 2;
252 width = CGRectGetMinX(cancelRect) - leftOffset;
255 return CGRectMake(leftOffset, CGRectGetMinY(rect),
width, CGRectGetHeight(rect));
263 - (CGRect)searchButtonRectForBounds:(CGRect)rect
268 return CGRectMake(inset.left - inset.right, inset.top - inset.bottom + (CGRectGetHeight(rect) - size.height) / 2, size.width, size.height);
276 - (CGRect)cancelButtonRectForBounds:(CGRect)rect
281 return CGRectMake(CGRectGetWidth(rect) - size.width + inset.left - inset.right, inset.top - inset.bottom + (CGRectGetHeight(rect) - size.width) / 2, size.height, size.height);
291 return _searchMenuTemplate;
299 - (void)setSearchMenuTemplate:(
CPMenu)aMenu
301 _searchMenuTemplate = aMenu;
304 [
self _loadRecentSearchList];
305 [
self _updateSearchMenu];
313 - (BOOL)sendsWholeSearchString
315 return _sendsWholeSearchString;
322 - (void)setSendsWholeSearchString:(BOOL)flag
324 _sendsWholeSearchString = flag;
331 - (BOOL)sendsSearchStringImmediately
333 return _sendsSearchStringImmediately;
340 - (void)setSendsSearchStringImmediately:(BOOL)flag
342 _sendsSearchStringImmediately = flag;
350 - (int)maximumRecents
352 return _maximumRecents;
359 - (void)setMaximumRecents:(
int)max
366 _maximumRecents = max;
373 - (CPArray)recentSearches
375 return _recentSearches;
383 - (void)setRecentSearches:(CPArray)searches
385 var max = MIN([
self maximumRecents], [searches count]),
386 searches = [searches subarrayWithRange:CPMakeRange(0, max)];
388 _recentSearches = searches;
389 [
self _autosaveRecentSearchList];
398 return _recentsAutosaveName;
407 if (_recentsAutosaveName != nil)
408 [
self _deregisterForAutosaveNotification];
410 _recentsAutosaveName =
name;
412 if (_recentsAutosaveName != nil)
413 [
self _registerForAutosaveNotification];
418 - (CGRect)contentRectForBounds:(CGRect)bounds
424 + (double)_keyboardDelayForPartialSearchString:(
CPString)string
426 return (6 - MIN([
string length], 4)) / 10;
436 return [
super isOpaque] && [_cancelButton isOpaque] && [_searchButton isOpaque];
439 - (void)_updateCancelButtonVisibility
441 [_cancelButton setHidden:([[
self stringValue] length] === 0)];
446 if (![
self sendsWholeSearchString])
448 if ([
self sendsSearchStringImmediately])
449 [
self _sendPartialString];
452 [_partialStringTimer invalidate];
453 var timeInterval = [
CPSearchField _keyboardDelayForPartialSearchString:[
self stringValue]];
457 selector:@selector(_sendPartialString)
463 [
self _updateCancelButtonVisibility];
466 - (void)_sendAction:(
id)sender
468 [
self sendAction:[
self action] to:[
self target]];
471 - (BOOL)sendAction:(
SEL)anAction to:(
id)anObject
475 [_partialStringTimer invalidate];
477 [
self _addStringToRecentSearches:[
self stringValue]];
478 [
self _updateCancelButtonVisibility];
481 - (void)_addStringToRecentSearches:(
CPString)string
483 if (
string === nil ||
string ===
@"" || [_recentSearches containsObject:
string])
487 [searches addObject:string];
488 [
self setRecentSearches:searches];
489 [
self _updateSearchMenu];
495 if (CGRectContainsPoint([
self frame], aPoint))
501 - (BOOL)resignFirstResponder
511 if (CGRectContainsPoint([
self searchButtonRectForBounds:[
self bounds]], point))
513 if (_searchMenuTemplate == nil)
515 if ([_searchButton target] && [_searchButton action])
516 [_searchButton mouseDown:anEvent];
518 [
self _sendAction:self];
523 else if (CGRectContainsPoint([
self cancelButtonRectForBounds:[
self bounds]], point))
524 [_cancelButton mouseDown:anEvent];
570 [item setTag:CPSearchFieldRecentsTitleMenuItemTag];
571 [item setEnabled:NO];
572 [template addItem:item];
577 [item setTag:CPSearchFieldRecentsMenuItemTag];
578 [item setTarget:self];
579 [template addItem:item];
584 [item setTag:CPSearchFieldClearRecentsMenuItemTag];
585 [item setTarget:self];
586 [template addItem:item];
591 [item setTag:CPSearchFieldNoRecentsMenuItemTag];
592 [item setEnabled:NO];
593 [template addItem:item];
598 - (void)_updateSearchMenu
600 if (_searchMenuTemplate === nil)
604 countOfRecents = [_recentSearches count],
605 numberOfItems = [_searchMenuTemplate numberOfItems];
607 for (var i = 0; i < numberOfItems; i++)
609 var item = [[_searchMenuTemplate itemAtIndex:i] copy];
614 if (countOfRecents === 0)
617 if ([menu numberOfItems] > 0)
618 [
self _addSeparatorToMenu:menu];
623 var itemAction =
@selector(_searchFieldSearch:);
625 for (var recentIndex = 0; recentIndex < countOfRecents; ++recentIndex)
631 [item setTarget:self];
632 [menu addItem:recentItem];
639 if (countOfRecents === 0)
642 if ([menu numberOfItems] > 0)
643 [
self _addSeparatorToMenu:menu];
645 [item setAction:@selector(_searchFieldClearRecents:)];
646 [item setTarget:self];
650 if (countOfRecents !== 0)
653 if ([menu numberOfItems] > 0)
654 [
self _addSeparatorToMenu:menu];
658 [item setEnabled:([item isEnabled] && [item action] != nil && [item target] != nil)];
662 [menu setDelegate:self];
667 - (void)_addSeparatorToMenu:(
CPMenu)aMenu
670 [separator setEnabled:NO];
676 _canResignFirstResponder = NO;
681 _canResignFirstResponder = YES;
688 if (_searchMenu === nil || [_searchMenu numberOfItems] === 0 || ![
self isEnabled])
691 var aFrame = [[
self superview] convertRect:[
self frame] toView:nil],
692 location = CGPointMake(aFrame.origin.x + 10, aFrame.origin.y + aFrame.size.height - 4);
696 [
self selectAll:nil];
700 - (void)_sendPartialString
702 [
super sendAction:[
self action] to:[
self target]];
703 [_partialStringTimer invalidate];
706 - (void)cancelOperation:(
id)sender
711 [
self _updateCancelButtonVisibility];
714 - (void)_searchFieldSearch:(
id)sender
716 var searchString = [[sender title] substringFromIndex:[RECENT_SEARCH_PREFIX length]];
719 [
self _addStringToRecentSearches:searchString];
721 [
self setObjectValue:searchString];
722 [
self _sendPartialString];
723 [
self selectAll:nil];
725 [
self _updateCancelButtonVisibility];
728 - (void)_searchFieldClearRecents:(
id)sender
730 [
self setRecentSearches:[CPArray array]];
731 [
self _updateSearchMenu];
732 [
self setStringValue:@""];
733 [
self _updateCancelButtonVisibility];
736 - (void)_registerForAutosaveNotification
741 - (void)_deregisterForAutosaveNotification
746 - (void)_autosaveRecentSearchList
748 if (_recentsAutosaveName != nil)
752 - (void)_updateAutosavedRecents:(
id)notification
754 var name = [notification object];
758 - (void)_loadRecentSearchList
760 var name = [
self recentsAutosaveName];
767 _recentSearches = list;
782 [_searchButton removeFromSuperview];
783 [_cancelButton removeFromSuperview];
792 [coder encodeBool:_sendsWholeSearchString forKey:CPSendsWholeSearchStringKey];
793 [coder encodeBool:_sendsSearchStringImmediately forKey:CPSendsSearchStringImmediatelyKey];
794 [coder encodeInt:_maximumRecents forKey:CPMaximumRecentsKey];
796 if (_recentsAutosaveName)
797 [coder encodeObject:_recentsAutosaveName forKey:CPRecentsAutosaveNameKey];
799 if (_searchMenuTemplate)
800 [coder encodeObject:_searchMenuTemplate forKey:CPSearchMenuTemplateKey];
805 if (
self = [super initWithCoder:coder])
808 _sendsWholeSearchString = [coder decodeBoolForKey:CPSendsWholeSearchStringKey];
809 _sendsSearchStringImmediately = [coder decodeBoolForKey:CPSendsSearchStringImmediatelyKey];
810 _maximumRecents = [coder decodeIntForKey:CPMaximumRecentsKey];
812 var
template = [coder decodeObjectForKey:CPSearchMenuTemplateKey];