API  0.9.8
 All Classes Files Functions Variables Typedefs 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 
28 
29 @optional
30 - (void)controlTextDidBeginEditing:(CPNotification)aNotification;
31 - (void)controlTextDidChange:(CPNotification)aNotification;
32 - (void)controlTextDidEndEditing:(CPNotification)aNotification;
33 - (void)controlTextDidFocus:(CPNotification)aNotification;
34 - (void)controlTextDidBlur:(CPNotification)aNotification;
35 - (BOOL)control:(CPControl)control didFailToFormatString:(CPString)string errorDescription:(CPString)error;
36 
37 @end
38 
39 @typedef CPTextAlignment
45 
46 @typedef CPControlSize
50 
51 @typedef CPLineBreakMode
58 
59 @typedef CPVerticalTextAlignment
63 
64 // Deprecated for use with images, use the CPImageScale constants
65 @typedef CPImageScaling
69 
74 
75 @typedef CPCellImagePosition
83 
87 
88 CPControlNormalBackgroundColor = "CPControlNormalBackgroundColor";
89 CPControlSelectedBackgroundColor = "CPControlSelectedBackgroundColor";
90 CPControlHighlightedBackgroundColor = "CPControlHighlightedBackgroundColor";
91 CPControlDisabledBackgroundColor = "CPControlDisabledBackgroundColor";
92 
93 CPControlTextDidBeginEditingNotification = "CPControlTextDidBeginEditingNotification";
94 CPControlTextDidChangeNotification = "CPControlTextDidChangeNotification";
95 CPControlTextDidEndEditingNotification = "CPControlTextDidEndEditingNotification";
96 
98 
104 @implementation CPControl : CPView
105 {
106  id _value;
107  CPFormatter _formatter;
108 
109  // Target-Action Support
110  id _target;
111  SEL _action;
112  int _sendActionOn;
113  BOOL _sendsActionOnEndEditing;
114 
115  // Mouse Tracking Support
116  BOOL _continuousTracking;
117  BOOL _trackingWasWithinFrame;
118  unsigned _trackingMouseDownFlags;
119  CGPoint _previousTrackingLocation;
120 
121  CPControlSize _controlSize;
122 }
123 
124 + (CPDictionary)themeAttributes
125 {
126  return @{
127  @"alignment": CPLeftTextAlignment,
128  @"vertical-alignment": CPTopVerticalTextAlignment,
129  @"line-break-mode": CPLineBreakByClipping,
130  @"text-color": [CPColor blackColor],
131  @"font": [CPFont systemFontOfSize:CPFontCurrentSystemSize],
132  @"text-shadow-color": [CPNull null],
133  @"text-shadow-offset": CGSizeMakeZero(),
134  @"image-position": CPImageLeft,
135  @"image-scaling": CPScaleToFit,
136  @"min-size": CGSizeMakeZero(),
137  @"max-size": CGSizeMake(-1.0, -1.0),
138  @"nib2cib-adjustment-frame": CGRectMakeZero()
139  };
140 }
141 
142 + (void)initialize
143 {
144  if (self !== [CPControl class])
145  return;
146 
147  [self exposeBinding:@"value"];
148  [self exposeBinding:@"objectValue"];
149  [self exposeBinding:@"stringValue"];
150  [self exposeBinding:@"integerValue"];
151  [self exposeBinding:@"intValue"];
152  [self exposeBinding:@"doubleValue"];
153  [self exposeBinding:@"floatValue"];
154 
155  [self exposeBinding:@"enabled"];
156 }
157 
158 + (Class)_binderClassForBinding:(CPString)aBinding
159 {
160  if (aBinding === CPValueBinding)
161  return [_CPValueBinder class];
162  else if ([aBinding hasPrefix:CPEnabledBinding])
163  return [CPMultipleValueAndBinding class];
164 
165  return [super _binderClassForBinding:aBinding];
166 }
167 
171 - (void)_continuouslyReverseSetBinding
172 {
173  var binderClass = [[self class] _binderClassForBinding:CPValueBinding],
174  theBinding = [binderClass getBinding:CPValueBinding forObject:self];
175 
176  if ([theBinding continuouslyUpdatesValue])
177  [theBinding reverseSetValueFor:@"objectValue"];
178 }
179 
180 - (void)_reverseSetBinding
181 {
182  var binderClass = [[self class] _binderClassForBinding:CPValueBinding],
183  theBinding = [binderClass getBinding:CPValueBinding forObject:self];
184 
185  [theBinding reverseSetValueFor:@"objectValue"];
186 }
187 
188 - (id)initWithFrame:(CGRect)aFrame
189 {
190  self = [super initWithFrame:aFrame];
191 
192  if (self)
193  {
194  _sendActionOn = CPLeftMouseUpMask;
195  _trackingMouseDownFlags = 0;
196  }
197 
198  return self;
199 }
200 
201 
202 #pragma mark -
203 #pragma mark Control Size
204 
208 - (CPControlSize)controlSize
209 {
210  return _controlSize;
211 }
212 
217 - (void)setControlSize:(CPControlSize)aControlSize
218 {
219  if (_controlSize === aControlSize)
220  return;
221 
222  [self unsetThemeState:[self _controlSizeThemeState]];
223  _controlSize = aControlSize;
224  [self setThemeState:[self _controlSizeThemeState]];
225 
226  [self setNeedsLayout];
227  [self setNeedsDisplay:YES];
228 }
229 
234 - (ThemeState)_controlSizeThemeState
235 {
236  switch(_controlSize)
237  {
238  case CPSmallControlSize:
240 
241  case CPMiniControlSize:
243 
245  default:
247  }
248 }
249 
255 - (void)_sizeToControlSize
256 {
257  var frameSize = [self frameSize],
258  minSize = [self currentValueForThemeAttribute:@"min-size"],
259  maxSize = [self currentValueForThemeAttribute:@"max-size"];
260 
261  if (minSize.width > 0)
262  {
263  frameSize.width = MAX(minSize.width, frameSize.width);
264 
265  if (maxSize.width > 0)
266  frameSize.width = MIN(maxSize.width, frameSize.width);
267  }
268 
269  if (minSize.height > 0)
270  {
271  frameSize.height = MAX(minSize.height, frameSize.height);
272 
273  if (maxSize.height > 0)
274  frameSize.height = MIN(maxSize.height, frameSize.height);
275  }
276 
277  [self setFrameSize:frameSize];
278 }
279 
280 
281 #pragma mark -
282 
288 - (void)setAction:(SEL)anAction
289 {
290  _action = anAction;
291 }
292 
296 - (SEL)action
297 {
298  return _action;
299 }
300 
306 - (void)setTarget:(id)aTarget
307 {
308  _target = aTarget;
309 }
310 
314 - (id)target
315 {
316  return _target;
317 }
318 
325 - (BOOL)sendAction:(SEL)anAction to:(id)anObject
326 {
327  [self _reverseSetBinding];
328 
329  var binding = [CPBinder getBinding:CPTargetBinding forObject:self];
330  [binding invokeAction];
331 
332  return [CPApp sendAction:anAction to:anObject from:self];
333 }
334 
335 - (int)sendActionOn:(int)mask
336 {
337  var previousMask = _sendActionOn;
338 
339  _sendActionOn = mask;
340 
341  return previousMask;
342 }
343 
347 - (BOOL)isContinuous
348 {
349  // Some subclasses should redefine this with CPLeftMouseDraggedMask
350  return (_sendActionOn & CPPeriodicMask) !== 0;
351 }
352 
356 - (void)setContinuous:(BOOL)flag
357 {
358  // Some subclasses should redefine this with CPLeftMouseDraggedMask
359  if (flag)
360  _sendActionOn |= CPPeriodicMask;
361  else
362  _sendActionOn &= ~CPPeriodicMask;
363 }
364 
368 - (BOOL)tracksMouseOutsideOfFrame
369 {
370  return NO;
371 }
372 
373 - (void)trackMouse:(CPEvent)anEvent
374 {
375  var type = [anEvent type],
376  currentLocation = [self convertPoint:[anEvent locationInWindow] fromView:nil],
377  isWithinFrame = [self tracksMouseOutsideOfFrame] || CGRectContainsPoint([self bounds], currentLocation);
378 
379  if (type === CPLeftMouseUp)
380  {
381  [self stopTracking:_previousTrackingLocation at:currentLocation mouseIsUp:YES];
382 
383  _trackingMouseDownFlags = 0;
384 
385  if (isWithinFrame)
386  [self setThemeState:CPThemeStateHovered];
387  }
388  else
389  {
390  [self unsetThemeState:CPThemeStateHovered];
391 
392  if (type === CPLeftMouseDown)
393  {
394  _trackingMouseDownFlags = [anEvent modifierFlags];
395  _continuousTracking = [self startTrackingAt:currentLocation];
396  }
397  else if (type === CPLeftMouseDragged)
398  {
399  if (isWithinFrame)
400  {
401  if (!_trackingWasWithinFrame)
402  _continuousTracking = [self startTrackingAt:currentLocation];
403 
404  else if (_continuousTracking)
405  _continuousTracking = [self continueTracking:_previousTrackingLocation at:currentLocation];
406  }
407  else
408  [self stopTracking:_previousTrackingLocation at:currentLocation mouseIsUp:NO];
409  }
410 
411  [CPApp setTarget:self selector:@selector(trackMouse:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
412  }
413 
414  if ((_sendActionOn & (1 << type)) && isWithinFrame)
415  [self sendAction:_action to:_target];
416 
417  _trackingWasWithinFrame = isWithinFrame;
418  _previousTrackingLocation = currentLocation;
419 }
420 
421 - (void)setState:(CPInteger)state
422 {
423 }
424 
425 - (CPInteger)nextState
426 {
427  return 0;
428 }
429 
435 - (void)performClick:(id)sender
436 {
437  if (![self isEnabled])
438  return;
439 
440  [self highlight:YES];
441  [self setState:[self nextState]];
442 
443  try
444  {
445  [self sendAction:[self action] to:[self target]];
446  }
447  catch (e)
448  {
449  throw e;
450  }
451  finally
452  {
454  }
455 }
456 
461 - (void)unhighlightButtonTimerDidFinish:(id)sender
462 {
463  [self highlight:NO];
464 }
465 
469 - (unsigned)mouseDownFlags
470 {
471  return _trackingMouseDownFlags;
472 }
473 
474 - (BOOL)startTrackingAt:(CGPoint)aPoint
475 {
476  [self highlight:YES];
477 
478  return (_sendActionOn & CPPeriodicMask) || (_sendActionOn & CPLeftMouseDraggedMask);
479 }
480 
481 - (BOOL)continueTracking:(CGPoint)lastPoint at:(CGPoint)aPoint
482 {
483  return (_sendActionOn & CPPeriodicMask) || (_sendActionOn & CPLeftMouseDraggedMask);
484 }
485 
486 - (void)stopTracking:(CGPoint)lastPoint at:(CGPoint)aPoint mouseIsUp:(BOOL)mouseIsUp
487 {
488  if (mouseIsUp)
489  [self highlight:NO];
490  else
491  [self highlight:YES];
492 }
493 
497 - (BOOL)acceptsFirstMouse:(CPEvent)anEvent
498 {
499  return [self isEnabled];
500 }
501 
502 - (void)mouseDown:(CPEvent)anEvent
503 {
504  if (![self isEnabled])
505  return;
506 
507  [self trackMouse:anEvent];
508 }
509 
510 - (void)mouseEntered:(CPEvent)anEvent
511 {
512  if (![self isEnabled])
513  return;
514 
515  [self setThemeState:CPThemeStateHovered];
516 }
517 
518 - (void)mouseExited:(CPEvent)anEvent
519 {
520  var currentLocation = [self convertPoint:[anEvent locationInWindow] fromView:nil],
521  isWithinFrame = [self tracksMouseOutsideOfFrame] || CGRectContainsPoint([self bounds], currentLocation);
522 
523  // Make sure we're not still in the frame because Cappuccino will sent mouseExited events
524  // for all of the (ephemeral) subviews of a view as well.
525  if (!isWithinFrame)
526  [self unsetThemeState:CPThemeStateHovered];
527 }
528 
532 - (id)objectValue
533 {
534  return _value;
535 }
536 
540 - (void)setObjectValue:(id)anObject
541 {
542  _value = anObject;
543 
544  [self setNeedsLayout];
545  [self setNeedsDisplay:YES];
546 }
547 
551 - (float)floatValue
552 {
553  var floatValue = parseFloat(_value, 10);
554  return isNaN(floatValue) ? 0.0 : floatValue;
555 }
556 
560 - (void)setFloatValue:(float)aValue
561 {
562  [self setObjectValue:aValue];
563 }
564 
568 - (double)doubleValue
569 {
570  var doubleValue = parseFloat(_value, 10);
571  return isNaN(doubleValue) ? 0.0 : doubleValue;
572 }
573 
577 - (void)setDoubleValue:(double)anObject
578 {
579  [self setObjectValue:anObject];
580 }
581 
585 - (int)intValue
586 {
587  var intValue = parseInt(_value, 10);
588  return isNaN(intValue) ? 0.0 : intValue;
589 }
590 
594 - (void)setIntValue:(int)anObject
595 {
596  [self setObjectValue:anObject];
597 }
598 
602 - (int)integerValue
603 {
604  var intValue = parseInt(_value, 10);
605  return isNaN(intValue) ? 0.0 : intValue;
606 }
607 
611 - (void)setIntegerValue:(int)anObject
612 {
613  [self setObjectValue:anObject];
614 }
615 
619 - (CPString)stringValue
620 {
621  if (_formatter && _value !== undefined)
622  {
623  var formattedValue = [self hasThemeState:CPThemeStateEditing] ? [_formatter editingStringForObjectValue:_value] : [_formatter stringForObjectValue:_value];
624 
625  if (formattedValue !== nil && formattedValue !== undefined)
626  return formattedValue;
627  }
628 
629  return (_value === undefined || _value === nil) ? @"" : String(_value);
630 }
631 
635 - (void)setStringValue:(CPString)aString
636 {
637  // Cocoa raises an invalid parameter assertion and returns if you pass nil.
638  if (aString === nil || aString === undefined)
639  {
640  CPLog.warn("nil or undefined sent to CPControl -setStringValue");
641  return;
642  }
643 
644  var value;
645 
646  if (_formatter)
647  {
648  value = nil;
649 
650  if ([_formatter getObjectValue:@ref(value) forString:aString errorDescription:nil] === NO)
651  {
652  // If the given string is non-empty and doesn't work, Cocoa tries an empty string.
653  if (!aString || [_formatter getObjectValue:@ref(value) forString:@"" errorDescription:nil] === NO)
654  value = undefined; // Means the value is invalid
655  }
656  }
657  else
658  value = aString;
659 
660  [self setObjectValue:value];
661 }
662 
663 - (void)takeDoubleValueFrom:(id)sender
664 {
665  if ([sender respondsToSelector:@selector(doubleValue)])
666  [self setDoubleValue:[sender doubleValue]];
667 }
668 
669 
670 - (void)takeFloatValueFrom:(id)sender
671 {
672  if ([sender respondsToSelector:@selector(floatValue)])
673  [self setFloatValue:[sender floatValue]];
674 }
675 
676 - (void)takeIntegerValueFrom:(id)sender
677 {
678  if ([sender respondsToSelector:@selector(integerValue)])
679  [self setIntegerValue:[sender integerValue]];
680 }
681 
682 - (void)takeIntValueFrom:(id)sender
683 {
684  if ([sender respondsToSelector:@selector(intValue)])
685  [self setIntValue:[sender intValue]];
686 }
687 
688 - (void)takeObjectValueFrom:(id)sender
689 {
690  if ([sender respondsToSelector:@selector(objectValue)])
691  [self setObjectValue:[sender objectValue]];
692 }
693 
694 - (void)takeStringValueFrom:(id)sender
695 {
696  if ([sender respondsToSelector:@selector(stringValue)])
697  [self setStringValue:[sender stringValue]];
698 }
699 
700 - (void)textDidBeginEditing:(CPNotification)note
701 {
702  //this looks to prevent false propagation of notifications for other objects
703  if ([note object] != self)
704  return;
705 
706  [[CPNotificationCenter defaultCenter] postNotificationName:CPControlTextDidBeginEditingNotification object:self userInfo:@{"CPFieldEditor": [note object]}];
707 }
708 
709 - (void)textDidChange:(CPNotification)note
710 {
711  //this looks to prevent false propagation of notifications for other objects
712  if ([note object] != self)
713  return;
714 
715  [[CPNotificationCenter defaultCenter] postNotificationName:CPControlTextDidChangeNotification object:self userInfo:@{"CPFieldEditor": [note object]}];
716 }
717 
718 - (void)textDidEndEditing:(CPNotification)note
719 {
720  //this looks to prevent false propagation of notifications for other objects
721  if ([note object] != self)
722  return;
723 
724  [self _reverseSetBinding];
725 
726  [[CPNotificationCenter defaultCenter] postNotificationName:CPControlTextDidEndEditingNotification object:self userInfo:[note userInfo]];
727 }
728 
734 - (unsigned)_currentTextMovement
735 {
736  var currentEvent = [CPApp currentEvent],
737  keyCode = [currentEvent keyCode],
738  modifierFlags = [currentEvent modifierFlags];
739 
740  switch (keyCode)
741  {
742  case CPEscapeKeyCode:
743  return CPCancelTextMovement;
744 
745  case CPLeftArrowKeyCode:
746  return CPLeftTextMovement;
747 
748  case CPRightArrowKeyCode:
749  return CPRightTextMovement;
750 
751  case CPUpArrowKeyCode:
752  return CPUpTextMovement;
753 
754  case CPDownArrowKeyCode:
755  return CPDownTextMovement;
756 
757  case CPReturnKeyCode:
758  return CPReturnTextMovement;
759 
760  case CPTabKeyCode:
761  if (modifierFlags & CPShiftKeyMask)
762  return CPBacktabTextMovement;
763 
764  return CPTabTextMovement;
765 
766  default:
767  return CPOtherTextMovement;
768  }
769 }
770 
782 - (void)setAlignment:(CPTextAlignment)alignment
783 {
784  [self setValue:alignment forThemeAttribute:@"alignment"];
785 }
786 
790 - (CPTextAlignment)alignment
791 {
792  return [self valueForThemeAttribute:@"alignment"];
793 }
794 
804 - (void)setVerticalAlignment:(CPTextVerticalAlignment)alignment
805 {
806  [self setValue:alignment forThemeAttribute:@"vertical-alignment"];
807 }
808 
812 - (CPTextVerticalAlignment)verticalAlignment
813 {
814  return [self valueForThemeAttribute:@"vertical-alignment"];
815 }
816 
829 - (void)setLineBreakMode:(CPLineBreakMode)mode
830 {
831  [self setValue:mode forThemeAttribute:@"line-break-mode"];
832 }
833 
837 - (CPLineBreakMode)lineBreakMode
838 {
839  return [self valueForThemeAttribute:@"line-break-mode"];
840 }
841 
847 - (void)setTextColor:(CPColor)aColor
848 {
849  [self setValue:aColor forThemeAttribute:@"text-color"];
850 }
851 
855 - (CPColor)textColor
856 {
857  return [self valueForThemeAttribute:@"text-color"];
858 }
859 
863 - (void)setTextShadowColor:(CPColor)aColor
864 {
865  [self setValue:aColor forThemeAttribute:@"text-shadow-color"];
866 }
867 
871 - (CPColor)textShadowColor
872 {
873  return [self valueForThemeAttribute:@"text-shadow-color"];
874 }
875 
881 - (void)setTextShadowOffset:(CGSize)offset
882 {
883  [self setValue:offset forThemeAttribute:@"text-shadow-offset"];
884 }
885 
889 - (CGSize)textShadowOffset
890 {
891  return [self valueForThemeAttribute:@"text-shadow-offset"];
892 }
893 
897 - (void)setFont:(CPFont)aFont
898 {
899  [self setValue:aFont forThemeAttribute:@"font"];
900 }
901 
905 - (CPFont)font
906 {
907  return [self valueForThemeAttribute:@"font"];
908 }
909 
923 - (void)setImagePosition:(CPCellImagePosition)position
924 {
925  [self setValue:position forThemeAttribute:@"image-position"];
926 }
927 
931 - (CPCellImagePosition)imagePosition
932 {
933  return [self valueForThemeAttribute:@"image-position"];
934 }
935 
946 - (void)setImageScaling:(CPImageScaling)scaling
947 {
948  [self setValue:scaling forThemeAttribute:@"image-scaling"];
949 }
950 
954 - (CPUInteger)imageScaling
955 {
956  return [self valueForThemeAttribute:@"image-scaling"];
957 }
958 
965 - (void)setEnabled:(BOOL)isEnabled
966 {
967  if (isEnabled)
968  [self unsetThemeState:CPThemeStateDisabled];
969  else
970  [self setThemeState:CPThemeStateDisabled];
971 }
972 
976 - (BOOL)isEnabled
977 {
978  return ![self hasThemeState:CPThemeStateDisabled];
979 }
980 
986 - (void)highlight:(BOOL)shouldHighlight
987 {
988  [self setHighlighted:shouldHighlight];
989 }
990 
996 - (void)setHighlighted:(BOOL)isHighlighted
997 {
998  if (isHighlighted)
999  [self setThemeState:CPThemeStateHighlighted];
1000  else
1001  [self unsetThemeState:CPThemeStateHighlighted];
1002 }
1003 
1007 - (BOOL)isHighlighted
1008 {
1009  return [self hasThemeState:CPThemeStateHighlighted];
1010 }
1011 
1012 @end
1013 
1014 var CPControlActionKey = @"CPControlActionKey",
1015  CPControlControlSizeKey = @"CPControlControlSizeKey",
1016  CPControlControlStateKey = @"CPControlControlStateKey",
1017  CPControlFormatterKey = @"CPControlFormatterKey",
1018  CPControlIsEnabledKey = @"CPControlIsEnabledKey",
1019  CPControlSendActionOnKey = @"CPControlSendActionOnKey",
1020  CPControlSendsActionOnEndEditingKey = @"CPControlSendsActionOnEndEditingKey",
1021  CPControlTargetKey = @"CPControlTargetKey",
1022  CPControlValueKey = @"CPControlValueKey",
1023 
1024  __Deprecated__CPImageViewImageKey = @"CPImageViewImageKey";
1025 
1026 @implementation CPControl (CPCoding)
1027 
1028 /*
1029  Initializes the control by unarchiving it from a coder.
1030 
1031  @param aCoder the coder from which to unarchive the control
1032  @return the initialized control
1033 */
1034 - (id)initWithCoder:(CPCoder)aCoder
1035 {
1036  self = [super initWithCoder:aCoder];
1037 
1038  if (self)
1039  {
1040  [self setObjectValue:[aCoder decodeObjectForKey:CPControlValueKey]];
1041 
1042  [self setTarget:[aCoder decodeObjectForKey:CPControlTargetKey]];
1043  [self setAction:[aCoder decodeObjectForKey:CPControlActionKey]];
1044 
1045  [self sendActionOn:[aCoder decodeIntForKey:CPControlSendActionOnKey]];
1046  [self setSendsActionOnEndEditing:[aCoder decodeBoolForKey:CPControlSendsActionOnEndEditingKey]];
1047 
1048  [self setFormatter:[aCoder decodeObjectForKey:CPControlFormatterKey]];
1049 
1050  [self setControlSize:[aCoder decodeIntForKey:CPControlControlSizeKey]];
1051  }
1052 
1053  return self;
1054 }
1055 
1056 /*
1057  Archives the control to the provided coder.
1058 
1059  @param aCoder the coder to which the control will be archived.
1060 */
1061 - (void)encodeWithCoder:(CPCoder)aCoder
1062 {
1063  [super encodeWithCoder:aCoder];
1064 
1065  if (_sendsActionOnEndEditing)
1066  [aCoder encodeBool:_sendsActionOnEndEditing forKey:CPControlSendsActionOnEndEditingKey];
1067 
1068  var objectValue = [self objectValue];
1069 
1070  if (objectValue !== nil)
1071  [aCoder encodeObject:objectValue forKey:CPControlValueKey];
1072 
1073  if (_target !== nil)
1074  [aCoder encodeConditionalObject:_target forKey:CPControlTargetKey];
1075 
1076  if (_action !== nil)
1077  [aCoder encodeObject:_action forKey:CPControlActionKey];
1078 
1079  [aCoder encodeInt:_sendActionOn forKey:CPControlSendActionOnKey];
1080 
1081  if (_formatter !== nil)
1082  [aCoder encodeObject:_formatter forKey:CPControlFormatterKey];
1083 
1084  [aCoder encodeInt:_controlSize forKey:CPControlControlSizeKey];
1085 }
1086 
1087 @end
1088 
1089 var _CPControlSizeIdentifiers = [],
1090  _CPControlCachedColorWithPatternImages = {},
1091  _CPControlCachedThreePartImagePattern = {};
1092 
1093 _CPControlSizeIdentifiers[CPRegularControlSize] = "Regular";
1094 _CPControlSizeIdentifiers[CPSmallControlSize] = "Small";
1095 _CPControlSizeIdentifiers[CPMiniControlSize] = "Mini";
1096 
1097 function _CPControlIdentifierForControlSize(aControlSize)
1098 {
1099  return _CPControlSizeIdentifiers[aControlSize];
1100 }
1101 
1102 function _CPControlColorWithPatternImage(sizes, aClassName)
1103 {
1104  var index = 1,
1105  count = arguments.length,
1106  identifier = "";
1107 
1108  for (; index < count; ++index)
1109  identifier += arguments[index];
1110 
1111  var color = _CPControlCachedColorWithPatternImages[identifier];
1112 
1113  if (!color)
1114  {
1115  var bundle = [CPBundle bundleForClass:[CPControl class]];
1116 
1117  color = [CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:aClassName + "/" + identifier + ".png"] size:sizes[identifier]]];
1118 
1119  _CPControlCachedColorWithPatternImages[identifier] = color;
1120  }
1121 
1122  return color;
1123 }
1124 
1125 function _CPControlThreePartImagePattern(isVertical, sizes, aClassName)
1126 {
1127  var index = 2,
1128  count = arguments.length,
1129  identifier = "";
1130 
1131  for (; index < count; ++index)
1132  identifier += arguments[index];
1133 
1134  var color = _CPControlCachedThreePartImagePattern[identifier];
1135 
1136  if (!color)
1137  {
1138  var bundle = [CPBundle bundleForClass:[CPControl class]],
1139  path = aClassName + "/" + identifier;
1140 
1141  sizes = sizes[identifier];
1142 
1144  [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:path + "0.png"] size:sizes[0]],
1145  [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:path + "1.png"] size:sizes[1]],
1146  [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:path + "2.png"] size:sizes[2]]
1147  ] isVertical:isVertical]];
1148 
1149  _CPControlCachedThreePartImagePattern[identifier] = color;
1150  }
1151 
1152  return color;
1153 }
1154 
1156 
1160 - (CPFormatter)formatter
1161 {
1162  return _formatter;
1163 }
1164 
1168 - (void)setFormatter:(CPFormatter)aValue
1169 {
1170  _formatter = aValue;
1171 }
1172 
1176 - (BOOL)sendsActionOnEndEditing
1177 {
1178  return _sendsActionOnEndEditing;
1179 }
1180 
1184 - (void)setSendsActionOnEndEditing:(BOOL)aValue
1185 {
1186  _sendsActionOnEndEditing = aValue;
1187 }
1188 
1189 @end