API  1.0.0
CPSearchField.j
Go to the documentation of this file.
1 /*
2  * CPSearchField.j
3  * AppKit
4  *
5  * Created by cacaodev.
6  * Copyright 2009.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 
24 
25 @global CPApp
26 
31 
32 var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotification";
33 
40 @implementation CPSearchField : CPTextField
41 {
42  CPButton _searchButton;
43  CPButton _cancelButton;
44  CPMenu _searchMenuTemplate;
45  CPMenu _searchMenu;
46 
47  CPString _recentsAutosaveName;
48  CPArray _recentSearches;
49 
50  int _maximumRecents;
51  BOOL _sendsWholeSearchString;
52  BOOL _sendsSearchStringImmediately;
53  BOOL _canResignFirstResponder;
54  CPTimer _partialStringTimer;
55 }
56 
57 + (CPString)defaultThemeClass
58 {
59  return @"searchfield"
60 }
61 
62 + (CPDictionary)themeAttributes
63 {
64  return @{
65  @"image-search": [CPNull null],
66  @"image-find": [CPNull null],
67  @"image-cancel": [CPNull null],
68  @"image-cancel-pressed": [CPNull null],
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
73  };
74 }
75 
76 + (Class)_binderClassForBinding:(CPString)aBinding
77 {
78  if (aBinding === CPPredicateBinding)
79  return [_CPSearchFieldPredicateBinder class];
80 
81  return [super _binderClassForBinding:aBinding];
82 }
83 
84 - (id)initWithFrame:(CGRect)frame
85 {
86  if (self = [super initWithFrame:frame])
87  {
88  _maximumRecents = 10;
89  _sendsWholeSearchString = NO;
90  _sendsSearchStringImmediately = NO;
91  _recentsAutosaveName = nil;
92 
93  [self _init];
94  }
95 
96  return self;
97 }
98 
99 - (void)_init
100 {
101  _recentSearches = [CPArray array];
102 
103  [self setBezeled:YES];
104  [self setBezelStyle:CPTextFieldRoundedBezel];
105  [self setBordered:YES];
106  [self setEditable:YES];
107  [self setContinuous:YES];
108 
109  var bounds = [self bounds],
110  cancelButton = [[CPButton alloc] initWithFrame:[self cancelButtonRectForBounds:bounds]],
111  searchButton = [[CPButton alloc] initWithFrame:[self searchButtonRectForBounds:bounds]];
112 
113  [self setCancelButton:cancelButton];
114  [self resetCancelButton];
115 
116  [self setSearchButton:searchButton];
117  [self resetSearchButton];
118 
119  _canResignFirstResponder = YES;
120 }
121 
122 
123 #pragma mark -
124 #pragma mark Override observers
125 
126 - (void)_removeObservers
127 {
128  if (!_isObserving)
129  return;
130 
131  [super _removeObservers];
132 
133  [[CPNotificationCenter defaultCenter] removeObserver:self name:CPControlTextDidChangeNotification object:self];
134 }
135 
136 - (void)_addObservers
137 {
138  if (_isObserving)
139  return;
140 
141  [super _addObservers];
142 
143  [[CPNotificationCenter defaultCenter] addObserver:self selector:@selector(_searchFieldTextDidChange:) name:CPControlTextDidChangeNotification object:self];
144 }
145 
146 // Managing Buttons
151 - (void)setSearchButton:(CPButton)button
152 {
153  if (button != _searchButton)
154  {
155  [_searchButton removeFromSuperview];
156  _searchButton = button;
157 
158  [_searchButton setFrame:[self searchButtonRectForBounds:[self bounds]]];
159  [_searchButton setAutoresizingMask:CPViewMaxXMargin];
160  [self addSubview:_searchButton];
161  }
162 }
163 
168 - (CPButton)searchButton
169 {
170  return _searchButton;
171 }
172 
177 - (void)resetSearchButton
178 {
179  var button = [self searchButton],
180  searchButtonImage = (_searchMenuTemplate === nil) ? [self currentValueForThemeAttribute:@"image-search"] : [self currentValueForThemeAttribute:@"image-find"];
181 
182  [button setBordered:NO];
183  [button setImageScaling:CPImageScaleAxesIndependently];
184  [button setImage:searchButtonImage];
185  [button setAutoresizingMask:CPViewMaxXMargin];
186 }
187 
192 - (void)setCancelButton:(CPButton)button
193 {
194  if (button != _cancelButton)
195  {
196  [_cancelButton removeFromSuperview];
197  _cancelButton = button;
198 
199  [_cancelButton setFrame:[self cancelButtonRectForBounds:[self bounds]]];
200  [_cancelButton setAutoresizingMask:CPViewMinXMargin];
201  [_cancelButton setTarget:self];
202  [_cancelButton setAction:@selector(cancelOperation:)];
203  [_cancelButton setButtonType:CPMomentaryChangeButton];
204  [self _updateCancelButtonVisibility];
205  [self addSubview:_cancelButton];
206  }
207 }
208 
213 - (CPButton)cancelButton
214 {
215  return _cancelButton;
216 }
217 
222 - (void)resetCancelButton
223 {
224  var button = [self cancelButton];
225  [button setBordered:NO];
226  [button setImageScaling:CPImageScaleAxesIndependently];
227  [button setImage:[self valueForThemeAttribute:@"image-cancel"]];
228  [button setAlternateImage:[self valueForThemeAttribute:@"image-cancel-pressed"]];
229  [button setAutoresizingMask:CPViewMinXMargin];
230  [button setTarget:self];
231  [button setAction:@selector(cancelOperation:)];
232 }
233 
234 // Custom Layout
241 - (CGRect)searchTextRectForBounds:(CGRect)rect
242 {
243  var leftOffset = 0,
244  width = CGRectGetWidth(rect),
245  bounds = [self bounds];
246 
247  if (_searchButton)
248  {
249  var searchBounds = [self searchButtonRectForBounds:bounds],
250  rightMargin = [self currentValueForThemeAttribute:@"search-right-margin"];
251  leftOffset = CGRectGetMaxX(searchBounds) + rightMargin;
252  }
253 
254  if (_cancelButton)
255  {
256  var cancelRect = [self cancelButtonRectForBounds:bounds];
257  width = CGRectGetMinX(cancelRect) - leftOffset;
258  }
259 
260  return CGRectMake(leftOffset, CGRectGetMinY(rect), width, CGRectGetHeight(rect));
261 }
262 
268 - (CGRect)searchButtonRectForBounds:(CGRect)rect
269 {
270  var size = [[self currentValueForThemeAttribute:@"image-search"] size] || CGSizeMakeZero(),
271  inset = [self currentValueForThemeAttribute:@"image-search-inset"];
272 
273  return CGRectMake(inset.left - inset.right, inset.top - inset.bottom + (CGRectGetHeight(rect) - size.height) / 2, size.width, size.height);
274 }
275 
281 - (CGRect)cancelButtonRectForBounds:(CGRect)rect
282 {
283  var size = [[self currentValueForThemeAttribute:@"image-cancel"] size] || CGSizeMakeZero(),
284  inset = [self currentValueForThemeAttribute:@"image-cancel-inset"];
285 
286  return CGRectMake(CGRectGetWidth(rect) - size.width + inset.left - inset.right, inset.top - inset.bottom + (CGRectGetHeight(rect) - size.width) / 2, size.height, size.height);
287 }
288 
289 // Managing Menu Templates
294 - (CPMenu)searchMenuTemplate
295 {
296  return _searchMenuTemplate;
297 }
298 
304 - (void)setSearchMenuTemplate:(CPMenu)aMenu
305 {
306  _searchMenuTemplate = aMenu;
307 
308  [self resetSearchButton];
309  [self _loadRecentSearchList];
310  [self _updateSearchMenu];
311 }
312 
313 // Managing Search Modes
318 - (BOOL)sendsWholeSearchString
319 {
320  return _sendsWholeSearchString;
321 }
322 
327 - (void)setSendsWholeSearchString:(BOOL)flag
328 {
329  _sendsWholeSearchString = flag;
330 }
331 
336 - (BOOL)sendsSearchStringImmediately
337 {
338  return _sendsSearchStringImmediately;
339 }
340 
345 - (void)setSendsSearchStringImmediately:(BOOL)flag
346 {
347  _sendsSearchStringImmediately = flag;
348 }
349 
350 // Managing Recent Search Strings
355 - (int)maximumRecents
356 {
357  return _maximumRecents;
358 }
359 
364 - (void)setMaximumRecents:(int)max
365 {
366  if (max > 254)
367  max = 254;
368  else if (max < 0)
369  max = 10;
370 
371  _maximumRecents = max;
372 }
373 
378 - (CPArray)recentSearches
379 {
380  return _recentSearches;
381 }
382 
388 - (void)setRecentSearches:(CPArray)searches
389 {
390  var max = MIN([self maximumRecents], [searches count]);
391 
392  searches = [searches subarrayWithRange:CPMakeRange(0, max)];
393  _recentSearches = searches;
394  [self _autosaveRecentSearchList];
395 }
396 
401 - (CPString)recentsAutosaveName
402 {
403  return _recentsAutosaveName;
404 }
405 
410 - (void)setRecentsAutosaveName:(CPString)name
411 {
412  if (_recentsAutosaveName != nil)
413  [self _deregisterForAutosaveNotification];
414 
415  _recentsAutosaveName = name;
416 
417  if (_recentsAutosaveName != nil)
418  [self _registerForAutosaveNotification];
419 }
420 
421 // Private methods and subclassing
422 
423 - (CGRect)contentRectForBounds:(CGRect)bounds
424 {
425  var superbounds = [super contentRectForBounds:bounds];
426  return [self searchTextRectForBounds:superbounds];
427 }
428 
429 + (double)_keyboardDelayForPartialSearchString:(CPString)string
430 {
431  return (6 - MIN([string length], 4)) / 10;
432 }
433 
435 {
436  return _searchMenu;
437 }
438 
439 - (BOOL)isOpaque
440 {
441  return [super isOpaque] && [_cancelButton isOpaque] && [_searchButton isOpaque];
442 }
443 
444 - (void)_updateCancelButtonVisibility
445 {
446  [_cancelButton setHidden:([[self stringValue] length] === 0)];
447 }
448 
449 - (void)_searchFieldTextDidChange:(CPNotification)aNotification
450 {
451  if (![self sendsWholeSearchString])
452  {
453  if ([self sendsSearchStringImmediately])
454  [self _sendPartialString];
455  else
456  {
457  [_partialStringTimer invalidate];
458  var timeInterval = [CPSearchField _keyboardDelayForPartialSearchString:[self stringValue]];
459 
460  _partialStringTimer = [CPTimer scheduledTimerWithTimeInterval:timeInterval
461  target:self
462  selector:@selector(_sendPartialString)
463  userInfo:nil
464  repeats:NO];
465  }
466  }
467 
468  [self _updateCancelButtonVisibility];
469 }
470 
471 - (void)_sendAction:(id)sender
472 {
473  [self sendAction:[self action] to:[self target]];
474 }
475 
476 - (BOOL)sendAction:(SEL)anAction to:(id)anObject
477 {
478  [super sendAction:anAction to:anObject];
479 
480  [_partialStringTimer invalidate];
481 
482  [self _addStringToRecentSearches:[self stringValue]];
483  [self _updateCancelButtonVisibility];
484 }
485 
486 - (void)_addStringToRecentSearches:(CPString)string
487 {
488  if (string === nil || string === @"" || [_recentSearches containsObject:string])
489  return;
490 
491  var searches = [CPMutableArray arrayWithArray:_recentSearches];
492  [searches addObject:string];
493  [self setRecentSearches:searches];
494  [self _updateSearchMenu];
495 }
496 
497 - (CPView)hitTest:(CGPoint)aPoint
498 {
499  // Make sure a hit anywhere within the search field returns the search field itself
500  if (CGRectContainsPoint([self frame], aPoint))
501  return self;
502  else
503  return nil;
504 }
505 
506 - (BOOL)resignFirstResponder
507 {
508  return _canResignFirstResponder && [super resignFirstResponder];
509 }
510 
511 - (void)mouseDown:(CPEvent)anEvent
512 {
513  var location = [anEvent locationInWindow],
514  point = [self convertPoint:location fromView:nil];
515 
516  if (CGRectContainsPoint([self searchButtonRectForBounds:[self bounds]], point))
517  {
518  if (_searchMenuTemplate == nil)
519  {
520  if ([_searchButton target] && [_searchButton action])
521  [_searchButton mouseDown:anEvent];
522  else
523  [self _sendAction:self];
524  }
525  else
526  [self _showMenu];
527  }
528  else if (CGRectContainsPoint([self cancelButtonRectForBounds:[self bounds]], point))
529  [_cancelButton mouseDown:anEvent];
530  else
531  [super mouseDown:anEvent];
532 }
533 
564 - (CPMenu)defaultSearchMenuTemplate
565 {
566  var template = [[CPMenu alloc] init],
567  item;
568 
569  item = [[CPMenuItem alloc] initWithTitle:@"Clear"
570  action:@selector(_searchFieldClearRecents:)
571  keyEquivalent:@""];
572  [item setTag:CPSearchFieldClearRecentsMenuItemTag];
573  [item setTarget:self];
574  [template addItem:item];
575 
576  item = [[CPMenuItem alloc] initWithTitle:@"Recent Searches"
577  action:nil
578  keyEquivalent:@""];
579  [item setTag:CPSearchFieldRecentsTitleMenuItemTag];
580  [item setEnabled:NO];
581  [template addItem:item];
582 
583  item = [[CPMenuItem alloc] initWithTitle:@"Recent search item"
584  action:@selector(_searchFieldSearch:)
585  keyEquivalent:@""];
586  [item setTag:CPSearchFieldRecentsMenuItemTag];
587  [item setTarget:self];
588  [template addItem:item];
589 
590  return template;
591 }
592 
593 - (void)_updateSearchMenu
594 {
595  if (_searchMenuTemplate === nil)
596  return;
597 
598  var menu = [[CPMenu alloc] init],
599  countOfRecents = [_recentSearches count],
600  numberOfItems = [_searchMenuTemplate numberOfItems];
601 
602  [menu setAutoenablesItems:NO];
603 
604  for (var i = 0; i < numberOfItems; i++)
605  {
606  var item = [[_searchMenuTemplate itemAtIndex:i] copy];
607 
608  switch ([item tag])
609  {
611  if (countOfRecents === 0)
612  continue;
613 
614  if ([menu numberOfItems] > 0)
615  [self _addSeparatorToMenu:menu];
616  break;
617 
619  {
620  var itemAction = @selector(_searchFieldSearch:);
621 
622  for (var recentIndex = 0; recentIndex < countOfRecents; ++recentIndex)
623  {
624  var recentItem = [[CPMenuItem alloc] initWithTitle:[_recentSearches objectAtIndex:recentIndex]
625  action:itemAction
626  keyEquivalent:[item keyEquivalent]];
627  [item setTarget:self];
628  [menu addItem:recentItem];
629  }
630 
631  continue;
632  }
633 
635  if (countOfRecents === 0)
636  continue;
637 
638  if ([menu numberOfItems] > 0)
639  [self _addSeparatorToMenu:menu];
640 
641  [item setAction:@selector(_searchFieldClearRecents:)];
642  [item setTarget:self];
643  break;
644 
646  if (countOfRecents !== 0)
647  continue;
648 
649  if ([menu numberOfItems] > 0)
650  [self _addSeparatorToMenu:menu];
651  break;
652  }
653 
654  [item setEnabled:([item isEnabled] && [item action] != nil && [item target] != nil)];
655  [menu addItem:item];
656  }
657 
658  [menu setDelegate:self];
659 
660  _searchMenu = menu;
661 }
662 
663 - (void)_addSeparatorToMenu:(CPMenu)aMenu
664 {
665  var separator = [CPMenuItem separatorItem];
666  [separator setEnabled:NO];
667  [aMenu addItem:separator];
668 }
669 
670 - (void)menuWillOpen:(CPMenu)menu
671 {
672  _canResignFirstResponder = NO;
673 }
674 
675 - (void)menuDidClose:(CPMenu)menu
676 {
677  _canResignFirstResponder = YES;
678 
679  [self becomeFirstResponder];
680 }
681 
682 - (void)_showMenu
683 {
684  if (_searchMenu === nil || [_searchMenu numberOfItems] === 0 || ![self isEnabled] || ([_recentSearches count] === 0))
685  return;
686 
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);
690 
691  var anEvent = [CPEvent mouseEventWithType:CPRightMouseDown location:location modifierFlags:0 timestamp:[[CPApp currentEvent] timestamp] windowNumber:[[self window] windowNumber] context:nil eventNumber:1 clickCount:1 pressure:0];
692 
693  [self selectAll:nil];
694  [CPMenu popUpContextMenu:_searchMenu withEvent:anEvent forView:self];
695 }
696 
697 - (void)_sendPartialString
698 {
699  [super sendAction:[self action] to:[self target]];
700  [_partialStringTimer invalidate];
701 }
702 
703 - (void)cancelOperation:(id)sender
704 {
705  [self setObjectValue:@""];
706  [self textDidChange:[CPNotification notificationWithName:CPControlTextDidChangeNotification object:self userInfo:nil]];
707 
708  [self _updateCancelButtonVisibility];
709 }
710 
711 - (void)_searchFieldSearch:(id)sender
712 {
713  var searchString = [sender title];
714 
715  if ([sender tag] != CPSearchFieldRecentsMenuItemTag)
716  [self _addStringToRecentSearches:searchString];
717 
718  [self setObjectValue:searchString];
719  [self _sendPartialString];
720  [self selectAll:nil];
721 
722  [self _updateCancelButtonVisibility];
723 }
724 
725 - (void)_searchFieldClearRecents:(id)sender
726 {
727  [self setRecentSearches:[CPArray array]];
728  [self _updateSearchMenu];
729  [self setStringValue:@""];
730  [self _updateCancelButtonVisibility];
731  }
732 
733 - (void)_registerForAutosaveNotification
734 {
735  [[CPNotificationCenter defaultCenter] addObserver:self selector:@selector(_updateAutosavedRecents:) name:CPAutosavedRecentsChangedNotification object:_recentsAutosaveName];
736 }
737 
738 - (void)_deregisterForAutosaveNotification
739 {
740  [[CPNotificationCenter defaultCenter] removeObserver:self name:CPAutosavedRecentsChangedNotification object:_recentsAutosaveName];
741 }
742 
743 - (void)_autosaveRecentSearchList
744 {
745  if (_recentsAutosaveName != nil)
746  [[CPNotificationCenter defaultCenter] postNotificationName:CPAutosavedRecentsChangedNotification object:_recentsAutosaveName];
747 }
748 
749 - (void)_updateAutosavedRecents:(id)notification
750 {
751  var name = [notification object];
752  [[CPUserDefaults standardUserDefaults] setObject:_recentSearches forKey:name];
753 }
754 
755 - (void)_loadRecentSearchList
756 {
757  var name = [self recentsAutosaveName];
758  if (name === nil)
759  return;
760 
761  var list = [[CPUserDefaults standardUserDefaults] objectForKey:name];
762 
763  if (list !== nil)
764  _recentSearches = list;
765 }
766 
767 - (void)unbind:(CPString)aBinding
768 {
769  [super unbind:aBinding];
770 
771  // our CPPredicateBinding binder adds a binding to the value.
772  // this private binding has to be also removed
773  if (aBinding === CPPredicateBinding)
774  [[[self class] _binderClassForBinding:aBinding] unbind:CPValueBinding forObject:self];
775 }
776 
777 @end
778 
779 #pragma mark -
780 
781 @implementation CPSearchField (CPTrackingArea)
782 {
783  CPTrackingArea _searchButtonTrackingArea;
784  CPTrackingArea _cancelButtonTrackingArea;
785 }
786 
787 - (void)updateTrackingAreas
788 {
789  if (_searchButtonTrackingArea)
790  {
791  [self removeTrackingArea:_searchButtonTrackingArea];
792  _searchButtonTrackingArea = nil;
793  }
794 
795  if (_cancelButtonTrackingArea)
796  {
797  [self removeTrackingArea:_cancelButtonTrackingArea];
798  _cancelButtonTrackingArea = nil;
799  }
800 
801  if (_searchButton)
802  {
803  _searchButtonTrackingArea = [[CPTrackingArea alloc] initWithRect:[_searchButton frame]
804  options:CPTrackingCursorUpdate | CPTrackingActiveInKeyWindow
805  owner:self
806  userInfo:@{ @"isButton": YES }];
807 
808  [self addTrackingArea:_searchButtonTrackingArea];
809  }
810 
811  if (_cancelButton)
812  {
813  _cancelButtonTrackingArea = [[CPTrackingArea alloc] initWithRect:[_cancelButton frame]
814  options:CPTrackingCursorUpdate | CPTrackingActiveInKeyWindow
815  owner:self
816  userInfo:@{ @"isButton": YES }];
817 
818  [self addTrackingArea:_cancelButtonTrackingArea];
819  }
820 
821  [super updateTrackingAreas];
822 }
823 
824 - (void)cursorUpdate:(CPEvent)anEvent
825 {
826  if ([[[anEvent trackingArea] userInfo] objectForKey:@"isButton"])
828  else
829  [super cursorUpdate:anEvent];
830 }
831 
832 @end
833 
834 #pragma mark -
835 
836 var CPRecentsAutosaveNameKey = @"CPRecentsAutosaveNameKey",
837  CPSendsWholeSearchStringKey = @"CPSendsWholeSearchStringKey",
838  CPSendsSearchStringImmediatelyKey = @"CPSendsSearchStringImmediatelyKey",
839  CPMaximumRecentsKey = @"CPMaximumRecentsKey",
840  CPSearchMenuTemplateKey = @"CPSearchMenuTemplateKey";
841 
842 @implementation CPSearchField (CPCoding)
843 
844 - (void)encodeWithCoder:(CPCoder)coder
845 {
846  [_searchButton removeFromSuperview];
847  [_cancelButton removeFromSuperview];
848 
849  [super encodeWithCoder:coder];
850 
851  if (_searchButton)
852  [self addSubview:_searchButton];
853  if (_cancelButton)
854  [self addSubview:_cancelButton];
855 
856  [coder encodeBool:_sendsWholeSearchString forKey:CPSendsWholeSearchStringKey];
857  [coder encodeBool:_sendsSearchStringImmediately forKey:CPSendsSearchStringImmediatelyKey];
858  [coder encodeInt:_maximumRecents forKey:CPMaximumRecentsKey];
859 
860  if (_recentsAutosaveName)
861  [coder encodeObject:_recentsAutosaveName forKey:CPRecentsAutosaveNameKey];
862 
863  if (_searchMenuTemplate)
864  [coder encodeObject:_searchMenuTemplate forKey:CPSearchMenuTemplateKey];
865 }
866 
867 - (id)initWithCoder:(CPCoder)coder
868 {
869  if (self = [super initWithCoder:coder])
870  {
871  [self setRecentsAutosaveName:[coder decodeObjectForKey:CPRecentsAutosaveNameKey]];
872  _sendsWholeSearchString = [coder decodeBoolForKey:CPSendsWholeSearchStringKey];
873  _sendsSearchStringImmediately = [coder decodeBoolForKey:CPSendsSearchStringImmediatelyKey];
874  _maximumRecents = [coder decodeIntForKey:CPMaximumRecentsKey];
875 
876  var template = [coder decodeObjectForKey:CPSearchMenuTemplateKey];
877 
878  if (template)
879  [self setSearchMenuTemplate:template];
880 
881  [self _init];
882  }
883 
884  return self;
885 }
886 
887 @end
888 
889 @implementation _CPSearchFieldPredicateBinder : CPBinder
890 {
891  CPArrayController _controller;
892  CPString _predicateFormat;
893 }
894 
895 - (void)setValue:(id)aValue forBinding:(CPString)aBinding
896 {
897  if (aBinding === CPPredicateBinding)
898  {
899  var options = [_info objectForKey:CPOptionsKey];
900 
901  _controller = [_info objectForKey:CPObservedObjectKey];
902  _predicateFormat = [options objectForKey:"CPPredicateFormat"];
903  [_source bind:CPValueBinding toObject:self withKeyPath:"searchFieldValue" options:nil];
904  }
905 }
906 
907 - (void)setSearchFieldValue:(CPString)aValue
908 {
909  var destination = [_info objectForKey:CPObservedObjectKey],
910  keyPath = [_info objectForKey:CPObservedKeyPathKey];
911 
912  var formatString = _predicateFormat.replace(/\$value/g, "%@");
913  [self suppressSpecificNotificationFromObject:destination keyPath:keyPath];
914 
915  if (aValue)
916  [_controller setFilterPredicate:[CPPredicate predicateWithFormat:formatString, aValue]];
917  else
918  [_controller setFilterPredicate:nil];
919 
920  [self unsuppressSpecificNotificationFromObject:destination keyPath:keyPath];
921 }
922 - (CPString)searchFieldValue
923 {
924  return [_source stringValue];
925 }
926 
927 @end
928 
CPSearchFieldClearRecentsMenuItemTag
Definition: CPSearchField.j:29
Definition: CPMenu.h:2
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)
Definition: CPEvent.j:130
void addSubview:(CPView aSubview)
Definition: CPView.j:536
void encodeWithCoder:(CPCoder aCoder)
Definition: CPTextField.j:2090
void setAction:(SEL anAction)
Definition: CPControl.j:282
var CPSendsSearchStringImmediatelyKey
void textDidChange:(CPNotification note)
Definition: CPTextField.j:1218
CGRect frame
An object representation of nil.
Definition: CPNull.h:2
void setTarget:(id aTarget)
Definition: CPControl.j:300
CPCursor arrowCursor()
Definition: CPCursor.j:246
void setBordered:(BOOL shouldBeBordered)
Definition: CPButton.j:768
BOOL becomeFirstResponder()
Definition: CPTextField.j:596
void resetSearchButton()
void addObserver:selector:name:object:(id anObserver, [selector] SEL aSelector, [name] CPString aNotificationName, [object] id anObject)
void setAlternateImage:(CPImage anImage)
Definition: CPButton.j:386
CGPoint locationInWindow()
Definition: CPEvent.j:290
CPMenuItem separatorItem()
Definition: CPMenuItem.j:536
int width
CGRect bounds()
Definition: CPView.j:1326
void postNotificationName:object:(CPString aNotificationName, [object] id anObject)
void setImageScaling:(CPImageScaling scaling)
Definition: CPControl.j:940
void addItem:(CPMenuItem aMenuItem)
Definition: CPMenu.j:312
CPNotificationCenter defaultCenter()
void setImage:(CPImage anImage)
Definition: CPButton.j:372
A mutable key-value pair collection.
Definition: CPDictionary.h:2
CGRect bounds()
Definition: CALayer.j:203
An immutable string (collection of characters).
Definition: CPString.h:2
CPNull null()
Definition: CPNull.j:51
CPButton searchButton()
CGPoint convertPoint:fromView:(CGPoint aPoint, [fromView] CPView aView)
Definition: CPView.j:2249
var CPAutosavedRecentsChangedNotification
Definition: CPSearchField.j:32
global CPApp CPSearchFieldRecentsTitleMenuItemTag
Definition: CPSearchField.j:27
BOOL sendAction:to:(SEL anAction, [to] id anObject)
Definition: CPControl.j:319
var CPMaximumRecentsKey
CGRect contentRectForBounds:(CGRect bounds)
Definition: CPTextField.j:1808
CGRect searchButtonRectForBounds:(CGRect rect)
void setAutoresizingMask:(unsigned aMask)
Definition: CPView.j:1511
BOOL isOpaque()
Definition: CPView.j:2814
int tag
CGRect searchTextRectForBounds:(CGRect rect)
CPButton cancelButton()
A notification that can be posted to a CPNotificationCenter.
Definition: CPNotification.h:2
CPTimer scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:(CPTimeInterval seconds, [target] id aTarget, [selector] SEL aSelector, [userInfo] id userInfo, [repeats] BOOL shouldRepeat)
Definition: CPTimer.j:58
CPString name
Definition: CPException.j:47
void setObjectValue:(id aValue)
Definition: CPTextField.j:1310
void setSearchMenuTemplate:(CPMenu aMenu)
CPString stringValue()
Definition: CPControl.j:613
var CPSearchMenuTemplateKey
A timer object that can send a message after the given time interval.
Definition: CPTimer.h:2
Defines methods for use when archiving & restoring (enc/decoding).
Definition: CPCoder.h:2
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)
Definition: CPView.j:3552
void setRecentsAutosaveName:(CPString name)
BOOL resignFirstResponder()
Definition: CPTextField.j:792
void mouseDown:(CPEvent anEvent)
Definition: CPTextField.j:996
id initWithTitle:action:keyEquivalent:(CPString aTitle, [action] SEL anAction, [keyEquivalent] CPString aKeyEquivalent)
Definition: CPMenuItem.j:121
void updateTrackingAreas()
Definition: CPTextField.j:2186
void removeObserver:name:object:(id anObserver, [name] CPString aNotificationName, [object] id anObject)
void set()
Definition: CPCursor.j:128
Definition: CPEvent.h:2
CPTrackingArea initWithRect:options:owner:userInfo:(CGRect aRect, [options] CPTrackingAreaOptions options, [owner] id owner, [userInfo] CPDictionary userInfo)
void popUpContextMenu:withEvent:forView:(CPMenu aMenu, [withEvent] CPEvent anEvent, [forView] CPView aView)
Definition: CPMenu.j:857
CPSearchFieldRecentsMenuItemTag
Definition: CPSearchField.j:28
CGRect cancelButtonRectForBounds:(CGRect rect)
CPSearchFieldNoRecentsMenuItemTag
Definition: CPSearchField.j:30
void removeTrackingArea:(CPTrackingArea trackingArea)
Definition: CPView.j:3570
CPMenu menu
id alloc()
Definition: CPObject.j:130
Definition: CPView.j:137
void cursorUpdate:(CPEvent anEvent)
Definition: CPTextField.j:2210