Cappuccino  1.0.0
 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 CPControlSize
43 
44 @typedef CPLineBreakMode
51 
52 @typedef CPVerticalTextAlignment
56 
57 // Deprecated for use with images, use the CPImageScale constants
58 @typedef CPImageScaling
62 
67 
68 @typedef CPCellImagePosition
76 
80 
81 CPControlNormalBackgroundColor = "CPControlNormalBackgroundColor";
82 CPControlSelectedBackgroundColor = "CPControlSelectedBackgroundColor";
83 CPControlHighlightedBackgroundColor = "CPControlHighlightedBackgroundColor";
84 CPControlDisabledBackgroundColor = "CPControlDisabledBackgroundColor";
85 
86 CPControlTextDidBeginEditingNotification = "CPControlTextDidBeginEditingNotification";
87 CPControlTextDidChangeNotification = "CPControlTextDidChangeNotification";
88 CPControlTextDidEndEditingNotification = "CPControlTextDidEndEditingNotification";
89 
91 
97 @implementation CPControl : CPView
98 {
99  id _value;
100  CPFormatter _formatter;
101 
102  // Target-Action Support
103  id _target;
104  SEL _action;
105  int _sendActionOn;
106  BOOL _sendsActionOnEndEditing;
107 
108  // Mouse Tracking Support
109  BOOL _continuousTracking;
110  BOOL _trackingWasWithinFrame;
111  unsigned _trackingMouseDownFlags;
112  CGPoint _previousTrackingLocation;
113 
114  CPControlSize _controlSize;
115 
116  CPWritingDirection _baseWritingDirection;
117 }
118 
119 + (CPDictionary)themeAttributes
120 {
121  return @{
122  @"alignment": CPLeftTextAlignment,
123  @"vertical-alignment": CPTopVerticalTextAlignment,
124  @"line-break-mode": CPLineBreakByClipping,
125  @"text-color": [CPColor blackColor],
126  @"font": [CPFont systemFontOfSize:CPFontCurrentSystemSize],
127  @"text-shadow-color": [CPNull null],
128  @"text-shadow-offset": CGSizeMakeZero(),
129  @"image-position": CPImageLeft,
130  @"image-scaling": CPScaleToFit,
131  @"min-size": CGSizeMakeZero(),
132  @"max-size": CGSizeMake(-1.0, -1.0),
133  @"nib2cib-adjustment-frame": CGRectMakeZero()
134  };
135 }
136 
137 + (void)initialize
138 {
139  if (self !== [CPControl class])
140  return;
141 
142  [self exposeBinding:@"value"];
143  [self exposeBinding:@"objectValue"];
144  [self exposeBinding:@"stringValue"];
145  [self exposeBinding:@"integerValue"];
146  [self exposeBinding:@"intValue"];
147  [self exposeBinding:@"doubleValue"];
148  [self exposeBinding:@"floatValue"];
149 
150  [self exposeBinding:@"enabled"];
151 }
152 
153 + (Class)_binderClassForBinding:(CPString)aBinding
154 {
155  if (aBinding === CPValueBinding)
156  return [_CPValueBinder class];
157  else if ([aBinding hasPrefix:CPEnabledBinding])
158  return [CPMultipleValueAndBinding class];
159 
160  return [super _binderClassForBinding:aBinding];
161 }
162 
166 - (void)_continuouslyReverseSetBinding
167 {
168  var binderClass = [[self class] _binderClassForBinding:CPValueBinding],
169  theBinding = [binderClass getBinding:CPValueBinding forObject:self];
170 
171  if ([theBinding continuouslyUpdatesValue])
172  [theBinding reverseSetValueFor:@"objectValue"];
173 }
174 
175 - (void)_reverseSetBinding
176 {
177  var binderClass = [[self class] _binderClassForBinding:CPValueBinding],
178  theBinding = [binderClass getBinding:CPValueBinding forObject:self];
179 
180  [theBinding reverseSetValueFor:@"objectValue"];
181 }
182 
183 - (id)initWithFrame:(CGRect)aFrame
184 {
185  self = [super initWithFrame:aFrame];
186 
187  if (self)
188  {
189  _sendActionOn = CPLeftMouseUpMask;
190  _trackingMouseDownFlags = 0;
191  }
192 
193  return self;
194 }
195 
196 #pragma mark -
197 #pragma mark Control Size
198 
202 - (CPControlSize)controlSize
203 {
204  return _controlSize;
205 }
206 
211 - (void)setControlSize:(CPControlSize)aControlSize
212 {
213  if (_controlSize === aControlSize)
214  return;
215 
216  [self unsetThemeState:[self _controlSizeThemeState]];
217  _controlSize = aControlSize;
218  [self setThemeState:[self _controlSizeThemeState]];
219 
220  [self setNeedsLayout];
221  [self setNeedsDisplay:YES];
222 }
223 
228 - (ThemeState)_controlSizeThemeState
229 {
230  switch (_controlSize)
231  {
232  case CPSmallControlSize:
234 
235  case CPMiniControlSize:
237 
239  default:
241  }
242 }
243 
249 - (void)_sizeToControlSize
250 {
251  var frameSize = [self frameSize],
252  minSize = [self currentValueForThemeAttribute:@"min-size"],
253  maxSize = [self currentValueForThemeAttribute:@"max-size"];
254 
255  if (minSize.width > 0)
256  {
257  frameSize.width = MAX(minSize.width, frameSize.width);
258 
259  if (maxSize.width > 0)
260  frameSize.width = MIN(maxSize.width, frameSize.width);
261  }
262 
263  if (minSize.height > 0)
264  {
265  frameSize.height = MAX(minSize.height, frameSize.height);
266 
267  if (maxSize.height > 0)
268  frameSize.height = MIN(maxSize.height, frameSize.height);
269  }
270 
271  [self setFrameSize:frameSize];
272 }
273 
274 
275 #pragma mark -
276 
282 - (void)setAction:(SEL)anAction
283 {
284  _action = anAction;
285 }
286 
290 - (SEL)action
291 {
292  return _action;
293 }
294 
300 - (void)setTarget:(id)aTarget
301 {
302  _target = aTarget;
303 }
304 
308 - (id)target
309 {
310  return _target;
311 }
312 
319 - (BOOL)sendAction:(SEL)anAction to:(id)anObject
320 {
321  [self _reverseSetBinding];
322 
323  var binding = [CPBinder getBinding:CPTargetBinding forObject:self];
324  [binding invokeAction];
325 
326  return [CPApp sendAction:anAction to:anObject from:self];
327 }
328 
329 - (int)sendActionOn:(int)mask
330 {
331  var previousMask = _sendActionOn;
332 
333  _sendActionOn = mask;
334 
335  return previousMask;
336 }
337 
341 - (BOOL)isContinuous
342 {
343  // Some subclasses should redefine this with CPLeftMouseDraggedMask
344  return (_sendActionOn & CPPeriodicMask) !== 0;
345 }
346 
350 - (void)setContinuous:(BOOL)flag
351 {
352  // Some subclasses should redefine this with CPLeftMouseDraggedMask
353  if (flag)
354  _sendActionOn |= CPPeriodicMask;
355  else
356  _sendActionOn &= ~CPPeriodicMask;
357 }
358 
362 - (BOOL)tracksMouseOutsideOfFrame
363 {
364  return NO;
365 }
366 
367 - (void)trackMouse:(CPEvent)anEvent
368 {
369  var type = [anEvent type],
370  currentLocation = [self convertPoint:[anEvent locationInWindow] fromView:nil],
371  isWithinFrame = [self tracksMouseOutsideOfFrame] || CGRectContainsPoint([self bounds], currentLocation);
372 
373  if (type === CPLeftMouseUp)
374  {
375  [self stopTracking:_previousTrackingLocation at:currentLocation mouseIsUp:YES];
376 
377  _trackingMouseDownFlags = 0;
378 
379  if (isWithinFrame)
380  [self setThemeState:CPThemeStateHovered];
381  }
382  else
383  {
384  [self unsetThemeState:CPThemeStateHovered];
385 
386  if (type === CPLeftMouseDown)
387  {
388  _trackingMouseDownFlags = [anEvent modifierFlags];
389  _continuousTracking = [self startTrackingAt:currentLocation];
390  }
391  else if (type === CPLeftMouseDragged)
392  {
393  if (isWithinFrame)
394  {
395  if (!_trackingWasWithinFrame)
396  _continuousTracking = [self startTrackingAt:currentLocation];
397 
398  else if (_continuousTracking)
399  _continuousTracking = [self continueTracking:_previousTrackingLocation at:currentLocation];
400  }
401  else
402  [self stopTracking:_previousTrackingLocation at:currentLocation mouseIsUp:NO];
403  }
404 
405  [CPApp setTarget:self selector:@selector(trackMouse:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
406  }
407 
408  if ((_sendActionOn & (1 << type)) && isWithinFrame)
409  [self sendAction:_action to:_target];
410 
411  _trackingWasWithinFrame = isWithinFrame;
412  _previousTrackingLocation = currentLocation;
413 }
414 
415 - (void)setState:(CPInteger)state
416 {
417 }
418 
419 - (CPInteger)nextState
420 {
421  return 0;
422 }
423 
429 - (void)performClick:(id)sender
430 {
431  if (![self isEnabled])
432  return;
433 
434  [self highlight:YES];
435  [self setState:[self nextState]];
436 
437  try
438  {
439  [self sendAction:[self action] to:[self target]];
440  }
441  catch (e)
442  {
443  throw e;
444  }
445  finally
446  {
448  }
449 }
450 
455 - (void)unhighlightButtonTimerDidFinish:(id)sender
456 {
457  [self highlight:NO];
458 }
459 
463 - (unsigned)mouseDownFlags
464 {
465  return _trackingMouseDownFlags;
466 }
467 
468 - (BOOL)startTrackingAt:(CGPoint)aPoint
469 {
470  [self highlight:YES];
471 
472  return (_sendActionOn & CPPeriodicMask) || (_sendActionOn & CPLeftMouseDraggedMask);
473 }
474 
475 - (BOOL)continueTracking:(CGPoint)lastPoint at:(CGPoint)aPoint
476 {
477  return (_sendActionOn & CPPeriodicMask) || (_sendActionOn & CPLeftMouseDraggedMask);
478 }
479 
480 - (void)stopTracking:(CGPoint)lastPoint at:(CGPoint)aPoint mouseIsUp:(BOOL)mouseIsUp
481 {
482  if (mouseIsUp)
483  [self highlight:NO];
484  else
485  [self highlight:YES];
486 }
487 
491 - (BOOL)acceptsFirstMouse:(CPEvent)anEvent
492 {
493  return [self isEnabled];
494 }
495 
496 - (void)mouseDown:(CPEvent)anEvent
497 {
498  if (![self isEnabled])
499  return;
500 
501  [self trackMouse:anEvent];
502 }
503 
504 - (void)mouseEntered:(CPEvent)anEvent
505 {
506  if (![self isEnabled])
507  return;
508 
509  [self setThemeState:CPThemeStateHovered];
510 }
511 
512 - (void)mouseExited:(CPEvent)anEvent
513 {
514  var currentLocation = [self convertPoint:[anEvent locationInWindow] fromView:nil],
515  isWithinFrame = [self tracksMouseOutsideOfFrame] || CGRectContainsPoint([self bounds], currentLocation);
516 
517  // Make sure we're not still in the frame because Cappuccino will sent mouseExited events
518  // for all of the (ephemeral) subviews of a view as well.
519  if (!isWithinFrame)
520  [self unsetThemeState:CPThemeStateHovered];
521 }
522 
526 - (id)objectValue
527 {
528  return _value;
529 }
530 
534 - (void)setObjectValue:(id)anObject
535 {
536  _value = anObject;
537 
538  [self setNeedsLayout];
539  [self setNeedsDisplay:YES];
540 }
541 
545 - (float)floatValue
546 {
547  var floatValue = parseFloat(_value, 10);
548  return isNaN(floatValue) ? 0.0 : floatValue;
549 }
550 
554 - (void)setFloatValue:(float)aValue
555 {
556  [self setObjectValue:aValue];
557 }
558 
562 - (double)doubleValue
563 {
564  var doubleValue = parseFloat(_value, 10);
565  return isNaN(doubleValue) ? 0.0 : doubleValue;
566 }
567 
571 - (void)setDoubleValue:(double)anObject
572 {
573  [self setObjectValue:anObject];
574 }
575 
579 - (int)intValue
580 {
581  var intValue = parseInt(_value, 10);
582  return isNaN(intValue) ? 0.0 : intValue;
583 }
584 
588 - (void)setIntValue:(int)anObject
589 {
590  [self setObjectValue:anObject];
591 }
592 
596 - (int)integerValue
597 {
598  var intValue = parseInt(_value, 10);
599  return isNaN(intValue) ? 0.0 : intValue;
600 }
601 
605 - (void)setIntegerValue:(int)anObject
606 {
607  [self setObjectValue:anObject];
608 }
609 
613 - (CPString)stringValue
614 {
615  if (_formatter && _value !== undefined)
616  {
617  var formattedValue = [self hasThemeState:CPThemeStateEditing] ? [_formatter editingStringForObjectValue:_value] : [_formatter stringForObjectValue:_value];
618 
619  if (formattedValue !== nil && formattedValue !== undefined)
620  return formattedValue;
621  }
622 
623  return (_value === undefined || _value === nil) ? @"" : String(_value);
624 }
625 
629 - (void)setStringValue:(CPString)aString
630 {
631  // Cocoa raises an invalid parameter assertion and returns if you pass nil.
632  if (aString === nil || aString === undefined)
633  {
634  CPLog.warn("nil or undefined sent to CPControl -setStringValue");
635  return;
636  }
637 
638  var value;
639 
640  if (_formatter)
641  {
642  value = nil;
643 
644  if ([_formatter getObjectValue:@ref(value) forString:aString errorDescription:nil] === NO)
645  {
646  // If the given string is non-empty and doesn't work, Cocoa tries an empty string.
647  if (!aString || [_formatter getObjectValue:@ref(value) forString:@"" errorDescription:nil] === NO)
648  value = undefined; // Means the value is invalid
649  }
650  }
651  else
652  value = aString;
653 
654  [self setObjectValue:value];
655 }
656 
657 - (void)takeDoubleValueFrom:(id)sender
658 {
659  if ([sender respondsToSelector:@selector(doubleValue)])
660  [self setDoubleValue:[sender doubleValue]];
661 }
662 
663 
664 - (void)takeFloatValueFrom:(id)sender
665 {
666  if ([sender respondsToSelector:@selector(floatValue)])
667  [self setFloatValue:[sender floatValue]];
668 }
669 
670 - (void)takeIntegerValueFrom:(id)sender
671 {
672  if ([sender respondsToSelector:@selector(integerValue)])
673  [self setIntegerValue:[sender integerValue]];
674 }
675 
676 - (void)takeIntValueFrom:(id)sender
677 {
678  if ([sender respondsToSelector:@selector(intValue)])
679  [self setIntValue:[sender intValue]];
680 }
681 
682 - (void)takeObjectValueFrom:(id)sender
683 {
684  if ([sender respondsToSelector:@selector(objectValue)])
685  [self setObjectValue:[sender objectValue]];
686 }
687 
688 - (void)takeStringValueFrom:(id)sender
689 {
690  if ([sender respondsToSelector:@selector(stringValue)])
691  [self setStringValue:[sender stringValue]];
692 }
693 
694 - (void)textDidBeginEditing:(CPNotification)note
695 {
696  //this looks to prevent false propagation of notifications for other objects
697  if ([note object] != self)
698  return;
699 
700  [[CPNotificationCenter defaultCenter] postNotificationName:CPControlTextDidBeginEditingNotification object:self userInfo:@{"CPFieldEditor": [note object]}];
701 }
702 
703 - (void)textDidChange:(CPNotification)note
704 {
705  //this looks to prevent false propagation of notifications for other objects
706  if ([note object] != self)
707  return;
708 
709  [[CPNotificationCenter defaultCenter] postNotificationName:CPControlTextDidChangeNotification object:self userInfo:@{"CPFieldEditor": [note object]}];
710 }
711 
712 - (void)textDidEndEditing:(CPNotification)note
713 {
714  //this looks to prevent false propagation of notifications for other objects
715  if ([note object] != self)
716  return;
717 
718  [self _reverseSetBinding];
719 
720  [[CPNotificationCenter defaultCenter] postNotificationName:CPControlTextDidEndEditingNotification object:self userInfo:[note userInfo]];
721 }
722 
728 - (unsigned)_currentTextMovement
729 {
730  var currentEvent = [CPApp currentEvent],
731  keyCode = [currentEvent keyCode],
732  modifierFlags = [currentEvent modifierFlags];
733 
734  switch (keyCode)
735  {
736  case CPEscapeKeyCode:
737  return CPCancelTextMovement;
738 
739  case CPLeftArrowKeyCode:
740  return CPLeftTextMovement;
741 
742  case CPRightArrowKeyCode:
743  return CPRightTextMovement;
744 
745  case CPUpArrowKeyCode:
746  return CPUpTextMovement;
747 
748  case CPDownArrowKeyCode:
749  return CPDownTextMovement;
750 
751  case CPReturnKeyCode:
752  return CPReturnTextMovement;
753 
754  case CPTabKeyCode:
755  if (modifierFlags & CPShiftKeyMask)
756  return CPBacktabTextMovement;
757 
758  return CPTabTextMovement;
759 
760  default:
761  return CPOtherTextMovement;
762  }
763 }
764 
776 - (void)setAlignment:(CPTextAlignment)alignment
777 {
778  [self setValue:alignment forThemeAttribute:@"alignment"];
779 }
780 
784 - (CPTextAlignment)alignment
785 {
786  return [self valueForThemeAttribute:@"alignment"];
787 }
788 
798 - (void)setVerticalAlignment:(CPTextVerticalAlignment)alignment
799 {
800  [self setValue:alignment forThemeAttribute:@"vertical-alignment"];
801 }
802 
806 - (CPTextVerticalAlignment)verticalAlignment
807 {
808  return [self valueForThemeAttribute:@"vertical-alignment"];
809 }
810 
823 - (void)setLineBreakMode:(CPLineBreakMode)mode
824 {
825  [self setValue:mode forThemeAttribute:@"line-break-mode"];
826 }
827 
831 - (CPLineBreakMode)lineBreakMode
832 {
833  return [self valueForThemeAttribute:@"line-break-mode"];
834 }
835 
841 - (void)setTextColor:(CPColor)aColor
842 {
843  [self setValue:aColor forThemeAttribute:@"text-color"];
844 }
845 
849 - (CPColor)textColor
850 {
851  return [self valueForThemeAttribute:@"text-color"];
852 }
853 
857 - (void)setTextShadowColor:(CPColor)aColor
858 {
859  [self setValue:aColor forThemeAttribute:@"text-shadow-color"];
860 }
861 
865 - (CPColor)textShadowColor
866 {
867  return [self valueForThemeAttribute:@"text-shadow-color"];
868 }
869 
875 - (void)setTextShadowOffset:(CGSize)offset
876 {
877  [self setValue:offset forThemeAttribute:@"text-shadow-offset"];
878 }
879 
883 - (CGSize)textShadowOffset
884 {
885  return [self valueForThemeAttribute:@"text-shadow-offset"];
886 }
887 
891 - (void)setFont:(CPFont)aFont
892 {
893  [self setValue:aFont forThemeAttribute:@"font"];
894 }
895 
899 - (CPFont)font
900 {
901  return [self valueForThemeAttribute:@"font"];
902 }
903 
917 - (void)setImagePosition:(CPCellImagePosition)position
918 {
919  [self setValue:position forThemeAttribute:@"image-position"];
920 }
921 
925 - (CPCellImagePosition)imagePosition
926 {
927  return [self valueForThemeAttribute:@"image-position"];
928 }
929 
940 - (void)setImageScaling:(CPImageScaling)scaling
941 {
942  [self setValue:scaling forThemeAttribute:@"image-scaling"];
943 }
944 
948 - (CPUInteger)imageScaling
949 {
950  return [self valueForThemeAttribute:@"image-scaling"];
951 }
952 
959 - (void)setEnabled:(BOOL)isEnabled
960 {
961  if (isEnabled)
962  [self unsetThemeState:CPThemeStateDisabled];
963  else
964  [self setThemeState:CPThemeStateDisabled];
965 }
966 
970 - (BOOL)isEnabled
971 {
972  return ![self hasThemeState:CPThemeStateDisabled];
973 }
974 
980 - (void)highlight:(BOOL)shouldHighlight
981 {
982  [self setHighlighted:shouldHighlight];
983 }
984 
990 - (void)setHighlighted:(BOOL)isHighlighted
991 {
992  if (isHighlighted)
993  [self setThemeState:CPThemeStateHighlighted];
994  else
995  [self unsetThemeState:CPThemeStateHighlighted];
996 }
997 
1001 - (BOOL)isHighlighted
1002 {
1003  return [self hasThemeState:CPThemeStateHighlighted];
1004 }
1005 
1006 
1007 #pragma mark -
1008 #pragma mark Base writing direction
1009 
1014 - (void)setBaseWritingDirection:(CPWritingDirection)writingDirection
1015 {
1016  if (writingDirection == _baseWritingDirection)
1017  return;
1018 
1019  [self willChangeValueForKey:@"baseWritingDirection"];
1020  _baseWritingDirection = writingDirection;
1021  [self didChangeValueForKey:@"baseWritingDirection"];
1022 
1023 #if PLATFORM(DOM)
1024 
1025  var style;
1026 
1027  switch (_baseWritingDirection)
1028  {
1030  style = "initial";
1031  break;
1032 
1034  style = "ltr";
1035  break;
1036 
1038  style = "rtl";
1039  break;
1040 
1041  default:
1042  style = "initial";
1043  }
1044 
1045  _DOMElement.style.direction = style;
1046 #endif
1047 }
1048 
1049 @end
1050 
1052 {
1053  CPTrackingArea _controlTrackingArea;
1054 }
1055 
1056 - (void)updateTrackingAreas
1057 {
1058  if (_controlTrackingArea)
1059  [self removeTrackingArea:_controlTrackingArea];
1060 
1061  _controlTrackingArea = [[CPTrackingArea alloc] initWithRect:CGRectMakeZero()
1062  options:CPTrackingMouseEnteredAndExited | CPTrackingActiveInKeyWindow | CPTrackingInVisibleRect
1063  owner:self
1064  userInfo:nil];
1065  [self addTrackingArea:_controlTrackingArea];
1066  [super updateTrackingAreas];
1067 }
1068 
1069 @end
1070 
1071 var CPControlActionKey = @"CPControlActionKey",
1072  CPControlControlSizeKey = @"CPControlControlSizeKey",
1073  CPControlControlStateKey = @"CPControlControlStateKey",
1074  CPControlFormatterKey = @"CPControlFormatterKey",
1075  CPControlIsEnabledKey = @"CPControlIsEnabledKey",
1076  CPControlSendActionOnKey = @"CPControlSendActionOnKey",
1077  CPControlSendsActionOnEndEditingKey = @"CPControlSendsActionOnEndEditingKey",
1078  CPControlTargetKey = @"CPControlTargetKey",
1079  CPControlValueKey = @"CPControlValueKey",
1080  CPControlBaseWrittingDirectionKey = @"CPControlBaseWrittingDirectionKey";
1081 
1082  __Deprecated__CPImageViewImageKey = @"CPImageViewImageKey";
1083 
1084 @implementation CPControl (CPCoding)
1085 
1086 /*
1087  Initializes the control by unarchiving it from a coder.
1088 
1089  @param aCoder the coder from which to unarchive the control
1090  @return the initialized control
1091 */
1092 - (id)initWithCoder:(CPCoder)aCoder
1093 {
1094  self = [super initWithCoder:aCoder];
1095 
1096  if (self)
1097  {
1098  [self setObjectValue:[aCoder decodeObjectForKey:CPControlValueKey]];
1099 
1100  [self setTarget:[aCoder decodeObjectForKey:CPControlTargetKey]];
1101  [self setAction:[aCoder decodeObjectForKey:CPControlActionKey]];
1102 
1103  [self sendActionOn:[aCoder decodeIntForKey:CPControlSendActionOnKey]];
1104  [self setSendsActionOnEndEditing:[aCoder decodeBoolForKey:CPControlSendsActionOnEndEditingKey]];
1105 
1106  [self setFormatter:[aCoder decodeObjectForKey:CPControlFormatterKey]];
1107 
1108  [self setControlSize:[aCoder decodeIntForKey:CPControlControlSizeKey]];
1109 
1110  [self setBaseWritingDirection:[aCoder decodeIntForKey:CPControlBaseWrittingDirectionKey]];
1111  }
1112 
1113  return self;
1114 }
1115 
1116 /*
1117  Archives the control to the provided coder.
1118 
1119  @param aCoder the coder to which the control will be archived.
1120 */
1121 - (void)encodeWithCoder:(CPCoder)aCoder
1122 {
1123  [super encodeWithCoder:aCoder];
1124 
1125  if (_sendsActionOnEndEditing)
1126  [aCoder encodeBool:_sendsActionOnEndEditing forKey:CPControlSendsActionOnEndEditingKey];
1127 
1128  var objectValue = [self objectValue];
1129 
1130  if (objectValue !== nil)
1131  [aCoder encodeObject:objectValue forKey:CPControlValueKey];
1132 
1133  if (_target !== nil)
1134  [aCoder encodeConditionalObject:_target forKey:CPControlTargetKey];
1135 
1136  if (_action !== nil)
1137  [aCoder encodeObject:_action forKey:CPControlActionKey];
1138 
1139  [aCoder encodeInt:_sendActionOn forKey:CPControlSendActionOnKey];
1140 
1141  if (_formatter !== nil)
1142  [aCoder encodeObject:_formatter forKey:CPControlFormatterKey];
1143 
1144  [aCoder encodeInt:_controlSize forKey:CPControlControlSizeKey];
1145 
1146  [aCoder encodeInt:_baseWritingDirection forKey:CPControlBaseWrittingDirectionKey];
1147 
1148 }
1149 
1150 @end
1151 
1152 var _CPControlSizeIdentifiers = [],
1153  _CPControlCachedColorWithPatternImages = {},
1154  _CPControlCachedThreePartImagePattern = {};
1155 
1156 _CPControlSizeIdentifiers[CPRegularControlSize] = "Regular";
1157 _CPControlSizeIdentifiers[CPSmallControlSize] = "Small";
1158 _CPControlSizeIdentifiers[CPMiniControlSize] = "Mini";
1159 
1160 function _CPControlIdentifierForControlSize(aControlSize)
1161 {
1162  return _CPControlSizeIdentifiers[aControlSize];
1163 }
1164 
1165 function _CPControlColorWithPatternImage(sizes, aClassName)
1166 {
1167  var index = 1,
1168  count = arguments.length,
1169  identifier = "";
1170 
1171  for (; index < count; ++index)
1172  identifier += arguments[index];
1173 
1174  var color = _CPControlCachedColorWithPatternImages[identifier];
1175 
1176  if (!color)
1177  {
1178  var bundle = [CPBundle bundleForClass:[CPControl class]];
1179 
1180  color = [CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:aClassName + "/" + identifier + ".png"] size:sizes[identifier]]];
1181 
1182  _CPControlCachedColorWithPatternImages[identifier] = color;
1183  }
1184 
1185  return color;
1186 }
1187 
1188 function _CPControlThreePartImagePattern(isVertical, sizes, aClassName)
1189 {
1190  var index = 2,
1191  count = arguments.length,
1192  identifier = "";
1193 
1194  for (; index < count; ++index)
1195  identifier += arguments[index];
1196 
1197  var color = _CPControlCachedThreePartImagePattern[identifier];
1198 
1199  if (!color)
1200  {
1201  var bundle = [CPBundle bundleForClass:[CPControl class]],
1202  path = aClassName + "/" + identifier;
1203 
1204  sizes = sizes[identifier];
1205 
1207  [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:path + "0.png"] size:sizes[0]],
1208  [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:path + "1.png"] size:sizes[1]],
1209  [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:path + "2.png"] size:sizes[2]]
1210  ] isVertical:isVertical]];
1211 
1212  _CPControlCachedThreePartImagePattern[identifier] = color;
1213  }
1214 
1215  return color;
1216 }
1217 
1219 
1223 - (CPFormatter)formatter
1224 {
1225  return _formatter;
1226 }
1227 
1231 - (void)setFormatter:(CPFormatter)aValue
1232 {
1233  _formatter = aValue;
1234 }
1235 
1239 - (BOOL)sendsActionOnEndEditing
1240 {
1241  return _sendsActionOnEndEditing;
1242 }
1243 
1247 - (void)setSendsActionOnEndEditing:(BOOL)aValue
1248 {
1249  _sendsActionOnEndEditing = aValue;
1250 }
1251 
1255 - (CPWritingDirection)baseWritingDirection
1256 {
1257  return _baseWritingDirection;
1258 }
1259 
1263 - (void)setBaseWritingDirection:(CPWritingDirection)aValue
1264 {
1265  _baseWritingDirection = aValue;
1266 }
1267 
1268 @end