API  0.9.7
 All Classes Files Functions Variables Macros Groups Pages
CPControl.j
Go to the documentation of this file.
1 /*
2  * CPControl.j
3  * AppKit
4  *
5  * Created by Francisco Tolmasky.
6  * Copyright 2008, 280 North, Inc.
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 
32 
36 
43 
47 
48 // Deprecated for use with images, use the CPImageScale constants
52 
57 
65 
69 
70 CPControlNormalBackgroundColor = "CPControlNormalBackgroundColor";
71 CPControlSelectedBackgroundColor = "CPControlSelectedBackgroundColor";
72 CPControlHighlightedBackgroundColor = "CPControlHighlightedBackgroundColor";
73 CPControlDisabledBackgroundColor = "CPControlDisabledBackgroundColor";
74 
75 CPControlTextDidBeginEditingNotification = "CPControlTextDidBeginEditingNotification";
76 CPControlTextDidChangeNotification = "CPControlTextDidChangeNotification";
77 CPControlTextDidEndEditingNotification = "CPControlTextDidEndEditingNotification";
78 
79 var CPControlBlackColor = [CPColor blackColor];
80 
87 @implementation CPControl : CPView
88 {
89  id _value;
90  CPFormatter _formatter;
91 
92  // Target-Action Support
93  id _target;
94  SEL _action;
95  int _sendActionOn;
96  BOOL _sendsActionOnEndEditing;
97 
98  // Mouse Tracking Support
99  BOOL _continuousTracking;
100  BOOL _trackingWasWithinFrame;
101  unsigned _trackingMouseDownFlags;
102  CGPoint _previousTrackingLocation;
103 }
104 
105 + (CPDictionary)themeAttributes
106 {
107  return @{
108  @"alignment": CPLeftTextAlignment,
109  @"vertical-alignment": CPTopVerticalTextAlignment,
110  @"line-break-mode": CPLineBreakByClipping,
111  @"text-color": [CPColor blackColor],
112  @"font": [CPFont systemFontOfSize:CPFontCurrentSystemSize],
113  @"text-shadow-color": [CPNull null],
114  @"text-shadow-offset": CGSizeMakeZero(),
115  @"image-position": CPImageLeft,
116  @"image-scaling": CPScaleToFit,
117  @"min-size": CGSizeMakeZero(),
118  @"max-size": CGSizeMake(-1.0, -1.0),
119  };
120 }
121 
122 + (void)initialize
123 {
124  if (self !== [CPControl class])
125  return;
126 
127  [self exposeBinding:@"value"];
128  [self exposeBinding:@"objectValue"];
129  [self exposeBinding:@"stringValue"];
130  [self exposeBinding:@"integerValue"];
131  [self exposeBinding:@"intValue"];
132  [self exposeBinding:@"doubleValue"];
133  [self exposeBinding:@"floatValue"];
134 
135  [self exposeBinding:@"enabled"];
136 }
137 
138 + (Class)_binderClassForBinding:(CPString)aBinding
139 {
140  if (aBinding === CPValueBinding)
141  return [_CPValueBinder class];
142  else if ([aBinding hasPrefix:CPEnabledBinding])
143  return [CPMultipleValueAndBinding class];
144 
145  return [super _binderClassForBinding:aBinding];
146 }
147 
151 - (void)_continuouslyReverseSetBinding
152 {
153  var binderClass = [[self class] _binderClassForBinding:CPValueBinding],
154  theBinding = [binderClass getBinding:CPValueBinding forObject:self];
155 
156  if ([theBinding continuouslyUpdatesValue])
157  [theBinding reverseSetValueFor:@"objectValue"];
158 }
159 
160 - (void)_reverseSetBinding
161 {
162  var binderClass = [[self class] _binderClassForBinding:CPValueBinding],
163  theBinding = [binderClass getBinding:CPValueBinding forObject:self];
164 
165  [theBinding reverseSetValueFor:@"objectValue"];
166 }
167 
168 - (id)initWithFrame:(CGRect)aFrame
169 {
170  self = [super initWithFrame:aFrame];
171 
172  if (self)
173  {
174  _sendActionOn = CPLeftMouseUpMask;
175  _trackingMouseDownFlags = 0;
176  }
177 
178  return self;
179 }
180 
186 - (void)setAction:(SEL)anAction
187 {
188  _action = anAction;
189 }
190 
194 - (SEL)action
195 {
196  return _action;
197 }
198 
204 - (void)setTarget:(id)aTarget
205 {
206  _target = aTarget;
207 }
208 
212 - (id)target
213 {
214  return _target;
215 }
216 
223 - (BOOL)sendAction:(SEL)anAction to:(id)anObject
224 {
225  [self _reverseSetBinding];
226 
227  var binding = [CPBinder getBinding:CPTargetBinding forObject:self];
228  [binding invokeAction];
229 
230  return [CPApp sendAction:anAction to:anObject from:self];
231 }
232 
233 - (int)sendActionOn:(int)mask
234 {
235  var previousMask = _sendActionOn;
236 
237  _sendActionOn = mask;
238 
239  return previousMask;
240 }
241 
245 - (BOOL)isContinuous
246 {
247  // Some subclasses should redefine this with CPLeftMouseDraggedMask
248  return (_sendActionOn & CPPeriodicMask) !== 0;
249 }
250 
254 - (void)setContinuous:(BOOL)flag
255 {
256  // Some subclasses should redefine this with CPLeftMouseDraggedMask
257  if (flag)
258  _sendActionOn |= CPPeriodicMask;
259  else
260  _sendActionOn &= ~CPPeriodicMask;
261 }
262 
266 - (BOOL)tracksMouseOutsideOfFrame
267 {
268  return NO;
269 }
270 
271 - (void)trackMouse:(CPEvent)anEvent
272 {
273  var type = [anEvent type],
274  currentLocation = [self convertPoint:[anEvent locationInWindow] fromView:nil],
275  isWithinFrame = [self tracksMouseOutsideOfFrame] || CGRectContainsPoint([self bounds], currentLocation);
276 
277  if (type === CPLeftMouseUp)
278  {
279  [self stopTracking:_previousTrackingLocation at:currentLocation mouseIsUp:YES];
280 
281  _trackingMouseDownFlags = 0;
282 
283  if (isWithinFrame)
284  [self setThemeState:CPThemeStateHovered];
285  }
286  else
287  {
288  [self unsetThemeState:CPThemeStateHovered];
289 
290  if (type === CPLeftMouseDown)
291  {
292  _trackingMouseDownFlags = [anEvent modifierFlags];
293  _continuousTracking = [self startTrackingAt:currentLocation];
294  }
295  else if (type === CPLeftMouseDragged)
296  {
297  if (isWithinFrame)
298  {
299  if (!_trackingWasWithinFrame)
300  _continuousTracking = [self startTrackingAt:currentLocation];
301 
302  else if (_continuousTracking)
303  _continuousTracking = [self continueTracking:_previousTrackingLocation at:currentLocation];
304  }
305  else
306  [self stopTracking:_previousTrackingLocation at:currentLocation mouseIsUp:NO];
307  }
308 
309  [CPApp setTarget:self selector:@selector(trackMouse:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
310  }
311 
312  if ((_sendActionOn & (1 << type)) && isWithinFrame)
313  [self sendAction:_action to:_target];
314 
315  _trackingWasWithinFrame = isWithinFrame;
316  _previousTrackingLocation = currentLocation;
317 }
318 
319 - (void)setState:(CPInteger)state
320 {
321 }
322 
323 - (CPInteger)nextState
324 {
325  return 0;
326 }
327 
333 - (void)performClick:(id)sender
334 {
335  if (![self isEnabled])
336  return;
337 
338  [self highlight:YES];
339  [self setState:[self nextState]];
340 
341  try
342  {
343  [self sendAction:[self action] to:[self target]];
344  }
345  catch (e)
346  {
347  throw e;
348  }
349  finally
350  {
352  }
353 }
354 
359 - (void)unhighlightButtonTimerDidFinish:(id)sender
360 {
361  [self highlight:NO];
362 }
363 
367 - (unsigned)mouseDownFlags
368 {
369  return _trackingMouseDownFlags;
370 }
371 
372 - (BOOL)startTrackingAt:(CGPoint)aPoint
373 {
374  [self highlight:YES];
375 
376  return (_sendActionOn & CPPeriodicMask) || (_sendActionOn & CPLeftMouseDraggedMask);
377 }
378 
379 - (BOOL)continueTracking:(CGPoint)lastPoint at:(CGPoint)aPoint
380 {
381  return (_sendActionOn & CPPeriodicMask) || (_sendActionOn & CPLeftMouseDraggedMask);
382 }
383 
384 - (void)stopTracking:(CGPoint)lastPoint at:(CGPoint)aPoint mouseIsUp:(BOOL)mouseIsUp
385 {
386  if (mouseIsUp)
387  [self highlight:NO];
388  else
389  [self highlight:YES];
390 }
391 
395 - (BOOL)acceptsFirstMouse:(CPEvent)anEvent
396 {
397  return [self isEnabled];
398 }
399 
400 - (void)mouseDown:(CPEvent)anEvent
401 {
402  if (![self isEnabled])
403  return;
404 
405  [self trackMouse:anEvent];
406 }
407 
408 - (void)mouseEntered:(CPEvent)anEvent
409 {
410  if (![self isEnabled])
411  return;
412 
413  [self setThemeState:CPThemeStateHovered];
414 }
415 
416 - (void)mouseExited:(CPEvent)anEvent
417 {
418  var currentLocation = [self convertPoint:[anEvent locationInWindow] fromView:nil],
419  isWithinFrame = [self tracksMouseOutsideOfFrame] || CGRectContainsPoint([self bounds], currentLocation);
420 
421  // Make sure we're not still in the frame because Cappuccino will sent mouseExited events
422  // for all of the (ephemeral) subviews of a view as well.
423  if (!isWithinFrame)
424  [self unsetThemeState:CPThemeStateHovered];
425 }
426 
430 - (id)objectValue
431 {
432  return _value;
433 }
434 
438 - (void)setObjectValue:(id)anObject
439 {
440  _value = anObject;
441 
442  [self setNeedsLayout];
443  [self setNeedsDisplay:YES];
444 }
445 
449 - (float)floatValue
450 {
451  var floatValue = parseFloat(_value, 10);
452  return isNaN(floatValue) ? 0.0 : floatValue;
453 }
454 
458 - (void)setFloatValue:(float)aValue
459 {
460  [self setObjectValue:aValue];
461 }
462 
466 - (double)doubleValue
467 {
468  var doubleValue = parseFloat(_value, 10);
469  return isNaN(doubleValue) ? 0.0 : doubleValue;
470 }
471 
475 - (void)setDoubleValue:(double)anObject
476 {
477  [self setObjectValue:anObject];
478 }
479 
483 - (int)intValue
484 {
485  var intValue = parseInt(_value, 10);
486  return isNaN(intValue) ? 0.0 : intValue;
487 }
488 
492 - (void)setIntValue:(int)anObject
493 {
494  [self setObjectValue:anObject];
495 }
496 
500 - (int)integerValue
501 {
502  var intValue = parseInt(_value, 10);
503  return isNaN(intValue) ? 0.0 : intValue;
504 }
505 
509 - (void)setIntegerValue:(int)anObject
510 {
511  [self setObjectValue:anObject];
512 }
513 
517 - (CPString)stringValue
518 {
519  if (_formatter && _value !== undefined)
520  {
521  var formattedValue = [self hasThemeState:CPThemeStateEditing] ? [_formatter editingStringForObjectValue:_value] : [_formatter stringForObjectValue:_value];
522 
523  if (formattedValue !== nil && formattedValue !== undefined)
524  return formattedValue;
525  }
526 
527  return (_value === undefined || _value === nil) ? @"" : String(_value);
528 }
529 
533 - (void)setStringValue:(CPString)aString
534 {
535  // Cocoa raises an invalid parameter assertion and returns if you pass nil.
536  if (aString === nil || aString === undefined)
537  {
538  CPLog.warn("nil or undefined sent to CPControl -setStringValue");
539  return;
540  }
541 
542  var value;
543 
544  if (_formatter)
545  {
546  value = nil;
547 
548  if ([_formatter getObjectValue:@ref(value) forString:aString errorDescription:nil] === NO)
549  {
550  // If the given string is non-empty and doesn't work, Cocoa tries an empty string.
551  if (!aString || [_formatter getObjectValue:@ref(value) forString:@"" errorDescription:nil] === NO)
552  value = undefined; // Means the value is invalid
553  }
554  }
555  else
556  value = aString;
557 
558  [self setObjectValue:value];
559 }
560 
561 - (void)takeDoubleValueFrom:(id)sender
562 {
563  if ([sender respondsToSelector:@selector(doubleValue)])
564  [self setDoubleValue:[sender doubleValue]];
565 }
566 
567 
568 - (void)takeFloatValueFrom:(id)sender
569 {
570  if ([sender respondsToSelector:@selector(floatValue)])
571  [self setFloatValue:[sender floatValue]];
572 }
573 
574 - (void)takeIntegerValueFrom:(id)sender
575 {
576  if ([sender respondsToSelector:@selector(integerValue)])
577  [self setIntegerValue:[sender integerValue]];
578 }
579 
580 - (void)takeIntValueFrom:(id)sender
581 {
582  if ([sender respondsToSelector:@selector(intValue)])
583  [self setIntValue:[sender intValue]];
584 }
585 
586 - (void)takeObjectValueFrom:(id)sender
587 {
588  if ([sender respondsToSelector:@selector(objectValue)])
589  [self setObjectValue:[sender objectValue]];
590 }
591 
592 - (void)takeStringValueFrom:(id)sender
593 {
594  if ([sender respondsToSelector:@selector(stringValue)])
595  [self setStringValue:[sender stringValue]];
596 }
597 
598 - (void)textDidBeginEditing:(CPNotification)note
599 {
600  //this looks to prevent false propagation of notifications for other objects
601  if ([note object] != self)
602  return;
603 
604  [[CPNotificationCenter defaultCenter] postNotificationName:CPControlTextDidBeginEditingNotification object:self userInfo:@{"CPFieldEditor": [note object]}];
605 }
606 
607 - (void)textDidChange:(CPNotification)note
608 {
609  //this looks to prevent false propagation of notifications for other objects
610  if ([note object] != self)
611  return;
612 
613  [[CPNotificationCenter defaultCenter] postNotificationName:CPControlTextDidChangeNotification object:self userInfo:@{"CPFieldEditor": [note object]}];
614 }
615 
616 - (void)textDidEndEditing:(CPNotification)note
617 {
618  //this looks to prevent false propagation of notifications for other objects
619  if ([note object] != self)
620  return;
621 
622  [self _reverseSetBinding];
623 
624  [[CPNotificationCenter defaultCenter] postNotificationName:CPControlTextDidEndEditingNotification object:self userInfo:[note userInfo]];
625 }
626 
632 - (unsigned)_currentTextMovement
633 {
634  var currentEvent = [CPApp currentEvent],
635  keyCode = [currentEvent keyCode],
636  modifierFlags = [currentEvent modifierFlags];
637 
638  switch (keyCode)
639  {
640  case CPEscapeKeyCode:
641  return CPCancelTextMovement;
642 
643  case CPLeftArrowKeyCode:
644  return CPLeftTextMovement;
645 
646  case CPRightArrowKeyCode:
647  return CPRightTextMovement;
648 
649  case CPUpArrowKeyCode:
650  return CPUpTextMovement;
651 
652  case CPDownArrowKeyCode:
653  return CPDownTextMovement;
654 
655  case CPReturnKeyCode:
656  return CPReturnTextMovement;
657 
658  case CPTabKeyCode:
659  if (modifierFlags & CPShiftKeyMask)
660  return CPBacktabTextMovement;
661 
662  return CPTabTextMovement;
663 
664  default:
665  return CPOtherTextMovement;
666  }
667 }
668 
680 - (void)setAlignment:(CPTextAlignment)alignment
681 {
682  [self setValue:alignment forThemeAttribute:@"alignment"];
683 }
684 
688 - (CPTextAlignment)alignment
689 {
690  return [self valueForThemeAttribute:@"alignment"];
691 }
692 
702 - (void)setVerticalAlignment:(CPTextVerticalAlignment)alignment
703 {
704  [self setValue:alignment forThemeAttribute:@"vertical-alignment"];
705 }
706 
710 - (CPTextVerticalAlignment)verticalAlignment
711 {
712  return [self valueForThemeAttribute:@"vertical-alignment"];
713 }
714 
727 - (void)setLineBreakMode:(CPLineBreakMode)mode
728 {
729  [self setValue:mode forThemeAttribute:@"line-break-mode"];
730 }
731 
735 - (CPLineBreakMode)lineBreakMode
736 {
737  return [self valueForThemeAttribute:@"line-break-mode"];
738 }
739 
745 - (void)setTextColor:(CPColor)aColor
746 {
747  [self setValue:aColor forThemeAttribute:@"text-color"];
748 }
749 
753 - (CPColor)textColor
754 {
755  return [self valueForThemeAttribute:@"text-color"];
756 }
757 
761 - (void)setTextShadowColor:(CPColor)aColor
762 {
763  [self setValue:aColor forThemeAttribute:@"text-shadow-color"];
764 }
765 
769 - (CPColor)textShadowColor
770 {
771  return [self valueForThemeAttribute:@"text-shadow-color"];
772 }
773 
779 - (void)setTextShadowOffset:(CGSize)offset
780 {
781  [self setValue:offset forThemeAttribute:@"text-shadow-offset"];
782 }
783 
787 - (CGSize)textShadowOffset
788 {
789  return [self valueForThemeAttribute:@"text-shadow-offset"];
790 }
791 
795 - (void)setFont:(CPFont)aFont
796 {
797  [self setValue:aFont forThemeAttribute:@"font"];
798 }
799 
803 - (CPFont)font
804 {
805  return [self valueForThemeAttribute:@"font"];
806 }
807 
821 - (void)setImagePosition:(CPCellImagePosition)position
822 {
823  [self setValue:position forThemeAttribute:@"image-position"];
824 }
825 
829 - (CPCellImagePosition)imagePosition
830 {
831  return [self valueForThemeAttribute:@"image-position"];
832 }
833 
844 - (void)setImageScaling:(CPImageScaling)scaling
845 {
846  [self setValue:scaling forThemeAttribute:@"image-scaling"];
847 }
848 
852 - (CPUInteger)imageScaling
853 {
854  return [self valueForThemeAttribute:@"image-scaling"];
855 }
856 
863 - (void)setEnabled:(BOOL)isEnabled
864 {
865  if (isEnabled)
866  [self unsetThemeState:CPThemeStateDisabled];
867  else
868  [self setThemeState:CPThemeStateDisabled];
869 }
870 
874 - (BOOL)isEnabled
875 {
876  return ![self hasThemeState:CPThemeStateDisabled];
877 }
878 
884 - (void)highlight:(BOOL)shouldHighlight
885 {
886  [self setHighlighted:shouldHighlight];
887 }
888 
894 - (void)setHighlighted:(BOOL)isHighlighted
895 {
896  if (isHighlighted)
897  [self setThemeState:CPThemeStateHighlighted];
898  else
899  [self unsetThemeState:CPThemeStateHighlighted];
900 }
901 
905 - (BOOL)isHighlighted
906 {
907  return [self hasThemeState:CPThemeStateHighlighted];
908 }
909 
910 @end
911 
912 var CPControlValueKey = @"CPControlValueKey",
913  CPControlControlStateKey = @"CPControlControlStateKey",
914  CPControlIsEnabledKey = @"CPControlIsEnabledKey",
915  CPControlTargetKey = @"CPControlTargetKey",
916  CPControlActionKey = @"CPControlActionKey",
917  CPControlSendActionOnKey = @"CPControlSendActionOnKey",
918  CPControlFormatterKey = @"CPControlFormatterKey",
919  CPControlSendsActionOnEndEditingKey = @"CPControlSendsActionOnEndEditingKey",
920 
921  __Deprecated__CPImageViewImageKey = @"CPImageViewImageKey";
922 
923 @implementation CPControl (CPCoding)
924 
925 /*
926  Initializes the control by unarchiving it from a coder.
927 
928  @param aCoder the coder from which to unarchive the control
929  @return the initialized control
930 */
931 - (id)initWithCoder:(CPCoder)aCoder
932 {
933  self = [super initWithCoder:aCoder];
934 
935  if (self)
936  {
937  [self setObjectValue:[aCoder decodeObjectForKey:CPControlValueKey]];
938 
939  [self setTarget:[aCoder decodeObjectForKey:CPControlTargetKey]];
940  [self setAction:[aCoder decodeObjectForKey:CPControlActionKey]];
941 
942  [self sendActionOn:[aCoder decodeIntForKey:CPControlSendActionOnKey]];
943  [self setSendsActionOnEndEditing:[aCoder decodeBoolForKey:CPControlSendsActionOnEndEditingKey]];
944 
945  [self setFormatter:[aCoder decodeObjectForKey:CPControlFormatterKey]];
946  }
947 
948  return self;
949 }
950 
951 /*
952  Archives the control to the provided coder.
953 
954  @param aCoder the coder to which the control will be archived.
955 */
956 - (void)encodeWithCoder:(CPCoder)aCoder
957 {
958  [super encodeWithCoder:aCoder];
959 
960  if (_sendsActionOnEndEditing)
961  [aCoder encodeBool:_sendsActionOnEndEditing forKey:CPControlSendsActionOnEndEditingKey];
962 
963  var objectValue = [self objectValue];
964 
965  if (objectValue !== nil)
966  [aCoder encodeObject:objectValue forKey:CPControlValueKey];
967 
968  if (_target !== nil)
969  [aCoder encodeConditionalObject:_target forKey:CPControlTargetKey];
970 
971  if (_action !== nil)
972  [aCoder encodeObject:_action forKey:CPControlActionKey];
973 
974  [aCoder encodeInt:_sendActionOn forKey:CPControlSendActionOnKey];
975 
976  if (_formatter !== nil)
977  [aCoder encodeObject:_formatter forKey:CPControlFormatterKey];
978 }
979 
980 @end
981 
982 var _CPControlSizeIdentifiers = [],
983  _CPControlCachedColorWithPatternImages = {},
984  _CPControlCachedThreePartImagePattern = {};
985 
986 _CPControlSizeIdentifiers[CPRegularControlSize] = "Regular";
987 _CPControlSizeIdentifiers[CPSmallControlSize] = "Small";
988 _CPControlSizeIdentifiers[CPMiniControlSize] = "Mini";
989 
990 function _CPControlIdentifierForControlSize(aControlSize)
991 {
992  return _CPControlSizeIdentifiers[aControlSize];
993 }
994 
995 function _CPControlColorWithPatternImage(sizes, aClassName)
996 {
997  var index = 1,
998  count = arguments.length,
999  identifier = "";
1000 
1001  for (; index < count; ++index)
1002  identifier += arguments[index];
1003 
1004  var color = _CPControlCachedColorWithPatternImages[identifier];
1005 
1006  if (!color)
1007  {
1008  var bundle = [CPBundle bundleForClass:[CPControl class]];
1009 
1010  color = [CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:aClassName + "/" + identifier + ".png"] size:sizes[identifier]]];
1011 
1012  _CPControlCachedColorWithPatternImages[identifier] = color;
1013  }
1014 
1015  return color;
1016 }
1017 
1018 function _CPControlThreePartImagePattern(isVertical, sizes, aClassName)
1019 {
1020  var index = 2,
1021  count = arguments.length,
1022  identifier = "";
1023 
1024  for (; index < count; ++index)
1025  identifier += arguments[index];
1026 
1027  var color = _CPControlCachedThreePartImagePattern[identifier];
1028 
1029  if (!color)
1030  {
1031  var bundle = [CPBundle bundleForClass:[CPControl class]],
1032  path = aClassName + "/" + identifier;
1033 
1034  sizes = sizes[identifier];
1035 
1036  color = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:[
1037  [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:path + "0.png"] size:sizes[0]],
1038  [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:path + "1.png"] size:sizes[1]],
1039  [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:path + "2.png"] size:sizes[2]]
1040  ] isVertical:isVertical]];
1041 
1042  _CPControlCachedThreePartImagePattern[identifier] = color;
1043  }
1044 
1045  return color;
1046 }
1047 
1049 
1053 - (CPFormatter)formatter
1054 {
1055  return _formatter;
1056 }
1057 
1061 - (void)setFormatter:(CPFormatter)aValue
1062 {
1063  _formatter = aValue;
1064 }
1065 
1069 - (BOOL)sendsActionOnEndEditing
1070 {
1071  return _sendsActionOnEndEditing;
1072 }
1073 
1077 - (void)setSendsActionOnEndEditing:(BOOL)aValue
1078 {
1079  _sendsActionOnEndEditing = aValue;
1080 }
1081 
1082 @end