API  0.9.6
 All Classes Files Functions Variables Macros Groups Pages
CPSegmentedControl.j
Go to the documentation of this file.
1 /*
2  * CPSegmentedControl.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 
29 
36 @implementation CPSegmentedControl : CPControl
37 {
38  CPArray _segments;
39  CPArray _themeStates;
40 
41  int _selectedSegment;
42  int _segmentStyle;
43  CPSegmentSwitchTracking _trackingMode;
44 
45  unsigned _trackingSegment;
46  BOOL _trackingHighlighted;
47 }
48 
49 + (CPString)defaultThemeClass
50 {
51  return "segmented-control";
52 }
53 
54 + (id)themeAttributes
55 {
56  return [CPDictionary dictionaryWithObjects:[CPCenterTextAlignment, CPCenterVerticalTextAlignment, CPImageLeft, CPImageScaleNone, _CGInsetMakeZero(), _CGInsetMakeZero(), [CPNull null], [CPNull null], [CPNull null], [CPNull null], 1.0, 24.0]
57  forKeys:[@"alignment", @"vertical-alignment", @"image-position", @"image-scaling", @"bezel-inset", @"content-inset", @"left-segment-bezel-color", @"right-segment-bezel-color", @"center-segment-bezel-color", @"divider-bezel-color", @"divider-thickness", @"default-height"]];
58 }
59 
60 - (id)initWithFrame:(CGRect)aRect
61 {
62  _segments = [];
63  _themeStates = [];
64 
65  self = [super initWithFrame:aRect];
66 
67  if (self)
68  {
69  _selectedSegment = -1;
70 
71  _trackingMode = CPSegmentSwitchTrackingSelectOne;
72  }
73 
74  return self;
75 }
76 
80 - (int)selectedTag
81 {
82  return [_segments[_selectedSegment] tag];
83 }
84 
85 // Specifying the number of segments
90 - (void)setSegmentCount:(unsigned)aCount
91 {
92  if (_segments.length == aCount)
93  return;
94 
95  var height = CGRectGetHeight([self bounds]),
96  dividersBefore = MAX(0, _segments.length - 1),
97  dividersAfter = MAX(0, aCount - 1);
98 
99  if (_segments.length < aCount)
100  {
101  for (var index = _segments.length; index < aCount; ++index)
102  {
103  _segments[index] = [[_CPSegmentItem alloc] init];
104  _themeStates[index] = CPThemeStateNormal;
105  }
106  }
107  else if (aCount < _segments.length)
108  {
109  _segments.length = aCount;
110  _themeStates.length = aCount;
111  }
112 
113  if (_selectedSegment >= _segments.length)
114  _selectedSegment = -1;
115 
116  var thickness = [self currentValueForThemeAttribute:@"divider-thickness"],
117  frame = [self frame],
118  widthOfAllSegments = 0,
119  dividerExtraSpace = ([_segments count] - 1) * thickness;
120 
121  for (var i = 0; i < [_segments count]; i++)
122  widthOfAllSegments += [_segments[i] width];
123 
124  [self setFrameSize:CGSizeMake(widthOfAllSegments + dividerExtraSpace, frame.size.height)];
125 
126  [self tileWithChangedSegment:0];
127 }
128 
132 - (unsigned)segmentCount
133 {
134  return _segments.length;
135 }
136 
137 // Specifying Selected Segment
143 - (void)setSelectedSegment:(unsigned)aSegment
144 {
145  // setSelected:forSegment throws the exception for us (if necessary)
146  [self setSelected:YES forSegment:aSegment];
147 }
148 
152 - (unsigned)selectedSegment
153 {
154  return _selectedSegment;
155 }
156 
160 - (BOOL)selectSegmentWithTag:(int)aTag
161 {
162  var index = 0;
163 
164  for (; index < _segments.length; ++index)
165  if (_segments[index].tag == aTag)
166  {
167  [self setSelectedSegment:index];
168 
169  return YES;
170  }
171 
172  return NO;
173 }
174 
175 // Specifying Tracking Mode
176 
177 - (BOOL)isTracking
178 {
179 
180 }
181 
182 - (void)setTrackingMode:(CPSegmentSwitchTracking)aTrackingMode
183 {
184  if (_trackingMode == aTrackingMode)
185  return;
186 
187  _trackingMode = aTrackingMode;
188 
189  if (_trackingMode == CPSegmentSwitchTrackingSelectOne)
190  {
191  var index = 0,
192  selected = NO;
193 
194  for (; index < _segments.length; ++index)
195  if ([_segments[index] selected])
196  if (selected)
197  [self setSelected:NO forSegment:index];
198  else
199  selected = YES;
200  }
201 
202  else if (_trackingMode == CPSegmentSwitchTrackingMomentary)
203  {
204  var index = 0;
205 
206  for (; index < _segments.length; ++index)
207  if ([_segments[index] selected])
208  [self setSelected:NO forSegment:index];
209  }
210 }
211 
215 - (CPSegmentSwitchTracking)trackingMode
216 {
217  return _trackingMode;
218 }
219 
220 // Working with Individual Segments
227 - (void)setWidth:(float)aWidth forSegment:(unsigned)aSegment
228 {
229  [_segments[aSegment] setWidth:aWidth];
230 
231  [self tileWithChangedSegment:aSegment];
232 }
233 
239 - (float)widthForSegment:(unsigned)aSegment
240 {
241  return [_segments[aSegment] width];
242 }
243 
250 - (void)setImage:(CPImage)anImage forSegment:(unsigned)aSegment
251 {
252  [_segments[aSegment] setImage:anImage];
253 
254  [self tileWithChangedSegment:aSegment];
255 }
256 
262 - (CPImage)imageForSegment:(unsigned)aSegment
263 {
264  return [_segments[aSegment] image];
265 }
266 
273 - (void)setLabel:(CPString)aLabel forSegment:(unsigned)aSegment
274 {
275  [_segments[aSegment] setLabel:aLabel];
276 
277  [self tileWithChangedSegment:aSegment];
278 }
279 
285 - (CPString)labelForSegment:(unsigned)aSegment
286 {
287  return [_segments[aSegment] label];
288 }
289 
296 - (void)setMenu:(CPMenu)aMenu forSegment:(unsigned)aSegment
297 {
298  [_segments[aSegment] setMenu:aMenu];
299 }
300 
306 - (CPMenu)menuForSegment:(unsigned)aSegment
307 {
308  return [_segments[aSegment] menu];
309 }
310 
318 - (void)setSelected:(BOOL)isSelected forSegment:(unsigned)aSegment
319 {
320  var segment = _segments[aSegment];
321 
322  // If we're already in this state, bail.
323  if ([segment selected] == isSelected)
324  return;
325 
326  [segment setSelected:isSelected];
327 
328  _themeStates[aSegment] = isSelected ? CPThemeStateSelected : CPThemeStateNormal;
329 
330  // We need to do some cleanup if we only allow one selection.
331  if (isSelected)
332  {
333  var oldSelectedSegment = _selectedSegment;
334 
335  _selectedSegment = aSegment;
336 
337  if (_trackingMode == CPSegmentSwitchTrackingSelectOne && oldSelectedSegment != aSegment && oldSelectedSegment != -1)
338  {
339  [_segments[oldSelectedSegment] setSelected:NO];
340  _themeStates[oldSelectedSegment] = CPThemeStateNormal;
341 
342  [self drawSegmentBezel:oldSelectedSegment highlight:NO];
343  }
344  }
345 
346  if (_trackingMode != CPSegmentSwitchTrackingMomentary)
347  [self drawSegmentBezel:aSegment highlight:NO];
348 
349  [self setNeedsLayout];
350  [self setNeedsDisplay:YES];
351 }
352 
358 - (BOOL)isSelectedForSegment:(unsigned)aSegment
359 {
360  return [_segments[aSegment] selected];
361 }
362 
369 - (void)setEnabled:(BOOL)shouldBeEnabled forSegment:(unsigned)aSegment
370 {
371  if ([_segments[aSegment] enabled] === shouldBeEnabled)
372  return;
373 
374  [_segments[aSegment] setEnabled:shouldBeEnabled];
375 
376  if (shouldBeEnabled)
377  _themeStates[aSegment] &= ~CPThemeStateDisabled;
378  else
379  _themeStates[aSegment] |= CPThemeStateDisabled;
380 
381  [self setNeedsLayout];
382  [self setNeedsDisplay:YES];
383 }
384 
390 - (BOOL)isEnabledForSegment:(unsigned)aSegment
391 {
392  return [_segments[aSegment] enabled];
393 }
394 
400 - (void)setTag:(int)aTag forSegment:(unsigned)aSegment
401 {
402  [_segments[aSegment] setTag:aTag];
403 }
404 
409 - (int)tagForSegment:(unsigned)aSegment
410 {
411  return [_segments[aSegment] tag];
412 }
413 
414 // Drawings
420 - (void)drawSegmentBezel:(int)aSegment highlight:(BOOL)shouldHighlight
421 {
422  if (shouldHighlight)
423  _themeStates[aSegment] |= CPThemeStateHighlighted;
424  else
425  _themeStates[aSegment] &= ~CPThemeStateHighlighted;
426 
427  [self setNeedsLayout];
428  [self setNeedsDisplay:YES];
429 }
430 
431 - (float)_leftOffsetForSegment:(unsigned)segment
432 {
433  var bezelInset = [self currentValueForThemeAttribute:@"bezel-inset"];
434 
435  if (segment == 0)
436  return bezelInset.left;
437 
438  var thickness = [self currentValueForThemeAttribute:@"divider-thickness"];
439 
440  return [self _leftOffsetForSegment:segment - 1] + [self widthForSegment:segment - 1] + thickness;
441 }
442 
443 - (unsigned)_indexOfLastSegment
444 {
445  var lastSegmentIndex = [_segments count] - 1;
446 
447  if (lastSegmentIndex < 0)
448  lastSegmentIndex = 0;
449 
450  return lastSegmentIndex;
451 }
452 
453 - (CGRect)rectForEphemeralSubviewNamed:(CPString)aName
454 {
455  var height = [self currentValueForThemeAttribute:@"default-height"],
456  contentInset = [self currentValueForThemeAttribute:@"content-inset"],
457  bezelInset = [self currentValueForThemeAttribute:@"bezel-inset"],
458  bounds = [self bounds];
459 
460  if (aName === "left-segment-bezel")
461  {
462  return CGRectMake(bezelInset.left, bezelInset.top, contentInset.left, height);
463  }
464  else if (aName === "right-segment-bezel")
465  {
466  return CPRectMake(CGRectGetWidth([self bounds]) - contentInset.right,
467  bezelInset.top,
468  contentInset.right,
469  height);
470  }
471  else if (aName.indexOf("segment-bezel") === 0)
472  {
473  var segment = parseInt(aName.substring("segment-bezel-".length), 10),
474  frame = CGRectCreateCopy([_segments[segment] frame]);
475 
476  if (segment === 0)
477  {
478  frame.origin.x += contentInset.left;
479  frame.size.width -= contentInset.left;
480  }
481 
482  if (segment === _segments.length - 1)
483  frame.size.width = CGRectGetWidth([self bounds]) - contentInset.right - frame.origin.x;
484 
485  return frame;
486  }
487  else if (aName.indexOf("divider-bezel") === 0)
488  {
489  var segment = parseInt(aName.substring("divider-bezel-".length), 10),
490  width = [self widthForSegment:segment],
491  left = [self _leftOffsetForSegment:segment],
492  thickness = [self currentValueForThemeAttribute:@"divider-thickness"];
493 
494  return CGRectMake(left + width, bezelInset.top, thickness, height);
495  }
496  else if (aName.indexOf("segment-content") === 0)
497  {
498  var segment = parseInt(aName.substring("segment-content-".length), 10);
499 
500  return [self contentFrameForSegment:segment];
501  }
502 
503  return [super rectForEphemeralSubviewNamed:aName];
504 }
505 
506 - (CPView)createEphemeralSubviewNamed:(CPString)aName
507 {
508  if ([aName hasPrefix:@"segment-content"])
509  return [[_CPImageAndTextView alloc] initWithFrame:_CGRectMakeZero()];
510 
511  return [[CPView alloc] initWithFrame:_CGRectMakeZero()];
512 }
513 
514 - (void)layoutSubviews
515 {
516  if (_segments.length <= 0)
517  return;
518 
519  var themeState = _themeStates[0];
520 
521  themeState |= _themeState & CPThemeStateDisabled;
522 
523  var leftCapColor = [self valueForThemeAttribute:@"left-segment-bezel-color"
524  inState:themeState],
525 
526  leftBezelView = [self layoutEphemeralSubviewNamed:@"left-segment-bezel"
527  positioned:CPWindowBelow
529 
530  [leftBezelView setBackgroundColor:leftCapColor];
531 
532  var themeState = _themeStates[_themeStates.length - 1];
533 
534  themeState |= _themeState & CPThemeStateDisabled;
535 
536  var rightCapColor = [self valueForThemeAttribute:@"right-segment-bezel-color"
537  inState:themeState],
538 
539  rightBezelView = [self layoutEphemeralSubviewNamed:@"right-segment-bezel"
540  positioned:CPWindowBelow
542 
543  [rightBezelView setBackgroundColor:rightCapColor];
544 
545  for (var i = 0, count = _themeStates.length; i < count; i++)
546  {
547  var themeState = _themeStates[i];
548 
549  themeState |= _themeState & CPThemeStateDisabled;
550 
551  var bezelColor = [self valueForThemeAttribute:@"center-segment-bezel-color"
552  inState:themeState],
553 
554  bezelView = [self layoutEphemeralSubviewNamed:"segment-bezel-" + i
555  positioned:CPWindowBelow
557 
558  [bezelView setBackgroundColor:bezelColor];
559 
560 
561  // layout image/title views
562  var segment = _segments[i],
563  contentView = [self layoutEphemeralSubviewNamed:@"segment-content-" + i
564  positioned:CPWindowAbove
565  relativeToEphemeralSubviewNamed:@"segment-bezel-" + i];
566 
567  [contentView setText:[segment label]];
568  [contentView setImage:[segment image]];
569 
570  [contentView setFont:[self valueForThemeAttribute:@"font" inState:themeState]];
571  [contentView setTextColor:[self valueForThemeAttribute:@"text-color" inState:themeState]];
572  [contentView setAlignment:[self valueForThemeAttribute:@"alignment" inState:themeState]];
573  [contentView setVerticalAlignment:[self valueForThemeAttribute:@"vertical-alignment" inState:themeState]];
574  [contentView setLineBreakMode:[self valueForThemeAttribute:@"line-break-mode" inState:themeState]];
575  [contentView setTextShadowColor:[self valueForThemeAttribute:@"text-shadow-color" inState:themeState]];
576  [contentView setTextShadowOffset:[self valueForThemeAttribute:@"text-shadow-offset" inState:themeState]];
577  [contentView setImageScaling:[self valueForThemeAttribute:@"image-scaling" inState:themeState]];
578 
579  if ([segment image] && [segment label])
580  [contentView setImagePosition:[self valueForThemeAttribute:@"image-position" inState:themeState]];
581  else if ([segment image])
582  [contentView setImagePosition:CPImageOnly];
583 
584  if (i == count - 1)
585  continue;
586 
587  var borderState = _themeStates[i] | _themeStates[i + 1];
588 
590 
591  borderState |= _themeState & CPThemeStateDisabled;
592 
593  var borderColor = [self valueForThemeAttribute:@"divider-bezel-color"
594  inState:borderState],
595 
596  borderView = [self layoutEphemeralSubviewNamed:"divider-bezel-" + i
597  positioned:CPWindowBelow
599 
600  [borderView setBackgroundColor:borderColor];
601  }
602 }
603 
604 
610 - (void)drawSegment:(int)aSegment highlight:(BOOL)shouldHighlight
611 {
612 }
613 
614 - (void)tileWithChangedSegment:(unsigned)aSegment
615 {
616  if (aSegment >= _segments.length)
617  return;
618 
619  var segment = _segments[aSegment],
620  segmentWidth = [segment width],
621  themeState = _themeStates[aSegment] | (_themeState & CPThemeStateDisabled),
622  contentInset = [self valueForThemeAttribute:@"content-inset" inState:themeState],
623  font = [self font];
624 
625  if (!segmentWidth)
626  {
627  if ([segment image] && [segment label])
628  segmentWidth = [[segment label] sizeWithFont:font].width + [[segment image] size].width + contentInset.left + contentInset.right;
629  else if (segment.image)
630  segmentWidth = [[segment image] size].width + contentInset.left + contentInset.right;
631  else if (segment.label)
632  segmentWidth = [[segment label] sizeWithFont:font].width + contentInset.left + contentInset.right;
633  else
634  segmentWidth = 0.0;
635  }
636 
637  var delta = segmentWidth - CGRectGetWidth([segment frame]);
638 
639  if (!delta)
640  {
641  [self setNeedsLayout];
642  [self setNeedsDisplay:YES];
643 
644  return;
645  }
646 
647  // Update control size
648  var frame = [self frame];
649 
650  [self setFrameSize:CGSizeMake(CGRectGetWidth(frame) + delta, CGRectGetHeight(frame))];
651 
652  // Update segment width
653  [segment setWidth:segmentWidth];
654  [segment setFrame:[self frameForSegment:aSegment]];
655 
656  // Update following segments widths
657  var index = aSegment + 1;
658 
659  for (; index < _segments.length; ++index)
660  {
661  [_segments[index] frame].origin.x += delta;
662 
663  [self drawSegmentBezel:index highlight:NO];
664  [self drawSegment:index highlight:NO];
665  }
666 
667  [self drawSegmentBezel:aSegment highlight:NO];
668  [self drawSegment:aSegment highlight:NO];
669 
670  [self setNeedsLayout];
671  [self setNeedsDisplay:YES];
672 }
673 
678 - (CGRect)frameForSegment:(unsigned)aSegment
679 {
680  return [self bezelFrameForSegment:aSegment];
681 }
682 
683 - (CGRect)bezelFrameForSegment:(unsigned)aSegment
684 {
685  var height = [self currentValueForThemeAttribute:@"default-height"],
686  bezelInset = [self currentValueForThemeAttribute:@"bezel-inset"],
687  width = [self widthForSegment:aSegment],
688  left = [self _leftOffsetForSegment:aSegment];
689 
690  return CGRectMake(left, bezelInset.top, width, height);
691 }
692 
693 - (CGRect)contentFrameForSegment:(unsigned)aSegment
694 {
695  var height = [self currentValueForThemeAttribute:@"default-height"],
696  contentInset = [self currentValueForThemeAttribute:@"content-inset"],
697  width = [self widthForSegment:aSegment],
698  left = [self _leftOffsetForSegment:aSegment];
699 
700  return CGRectMake(left + contentInset.left, contentInset.top, width - contentInset.left - contentInset.right, height - contentInset.top - contentInset.bottom);
701 }
702 
708 - (unsigned)testSegment:(CGPoint)aPoint
709 {
710  var location = [self convertPoint:aPoint fromView:nil],
711  count = _segments.length;
712 
713  while (count--)
714  if (CGRectContainsPoint([_segments[count] frame], aPoint))
715  return count;
716 
717  if (_segments.length)
718  {
719  var adjustedLastFrame = CGRectCreateCopy([_segments[_segments.length - 1] frame]);
720  adjustedLastFrame.size.width = CGRectGetWidth([self bounds]) - adjustedLastFrame.origin.x;
721 
722  if (CGRectContainsPoint(adjustedLastFrame, aPoint))
723  return _segments.length - 1;
724  }
725 
726  return -1;
727 }
728 
729 - (void)mouseDown:(CPEvent)anEvent
730 {
731  if (![self isEnabled])
732  return;
733 
734  [self trackSegment:anEvent];
735 }
736 
737 // FIXME: this should be fixed way up in cpbutton/cpcontrol.
738 - (void)mouseUp:(CPEvent)anEvent
739 {
740 }
741 
746 - (void)trackSegment:(CPEvent)anEvent
747 {
748  var type = [anEvent type],
749  location = [self convertPoint:[anEvent locationInWindow] fromView:nil];
750 
751  if (type == CPLeftMouseUp)
752  {
753  if (_trackingSegment == -1)
754  return;
755 
756  if (_trackingSegment === [self testSegment:location])
757  {
758  if (_trackingMode == CPSegmentSwitchTrackingSelectAny)
759  {
760  [self setSelected:![self isSelectedForSegment:_trackingSegment] forSegment:_trackingSegment];
761 
762  // With ANY, _selectedSegment means last pressed.
763  _selectedSegment = _trackingSegment;
764  }
765  else
766  [self setSelected:YES forSegment:_trackingSegment];
767 
768  [self sendAction:[self action] to:[self target]];
769 
770  if (_trackingMode == CPSegmentSwitchTrackingMomentary)
771  {
772  [self setSelected:NO forSegment:_trackingSegment];
773 
774  _selectedSegment = -1;
775  }
776  }
777 
778  [self drawSegmentBezel:_trackingSegment highlight:NO];
779 
780  _trackingSegment = -1;
781 
782  return;
783  }
784 
785  if (type == CPLeftMouseDown)
786  {
787  var trackingSegment = [self testSegment:location];
788  if (trackingSegment > -1 && [self isEnabledForSegment:trackingSegment])
789  {
790  _trackingHighlighted = YES;
791  _trackingSegment = trackingSegment;
792  [self drawSegmentBezel:_trackingSegment highlight:YES];
793  }
794  }
795 
796  else if (type == CPLeftMouseDragged)
797  {
798  if (_trackingSegment == -1)
799  return;
800 
801  var highlighted = [self testSegment:location] === _trackingSegment;
802  if (highlighted != _trackingHighlighted)
803  {
804  _trackingHighlighted = highlighted;
805 
806  [self drawSegmentBezel:_trackingSegment highlight:_trackingHighlighted];
807  }
808  }
809 
810  [CPApp setTarget:self selector:@selector(trackSegment:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
811 }
812 
813 - (void)setFont:(CPFont)aFont
814 {
815  [super setFont:aFont];
816 
817  [self tileWithChangedSegment:0];
818 }
819 
820 @end
821 
822 var CPSegmentedControlSegmentsKey = "CPSegmentedControlSegmentsKey",
823  CPSegmentedControlSelectedKey = "CPSegmentedControlSelectedKey",
824  CPSegmentedControlSegmentStyleKey = "CPSegmentedControlSegmentStyleKey",
825  CPSegmentedControlTrackingModeKey = "CPSegmentedControlTrackingModeKey";
826 
828 
829 - (id)initWithCoder:(CPCoder)aCoder
830 {
831  self = [super initWithCoder:aCoder];
832 
833  if (self)
834  {
835  var frame = [self frame],
836  originalWidth = frame.size.width;
837 
838  frame.size.width = 0;
839 
840  [self setFrame:frame];
841 
842  _segments = [aCoder decodeObjectForKey:CPSegmentedControlSegmentsKey];
843  _segmentStyle = [aCoder decodeIntForKey:CPSegmentedControlSegmentStyleKey];
844  _themeStates = [];
845 
846  if ([aCoder containsValueForKey:CPSegmentedControlSelectedKey])
847  _selectedSegment = [aCoder decodeIntForKey:CPSegmentedControlSelectedKey];
848  else
849  _selectedSegment = -1;
850 
851  if ([aCoder containsValueForKey:CPSegmentedControlTrackingModeKey])
852  _trackingMode = [aCoder decodeIntForKey:CPSegmentedControlTrackingModeKey];
853  else
854  _trackingMode = CPSegmentSwitchTrackingSelectOne;
855 
856  // HACK
857 
858  for (var i = 0; i < _segments.length; i++)
859  {
860  _themeStates[i] = [_segments[i] selected] ? CPThemeStateSelected : CPThemeStateNormal;
861  [self tileWithChangedSegment:i];
862  }
863 
864  var difference = MAX(originalWidth - [self frame].size.width, 0.0),
865  remainingWidth = FLOOR(difference / _segments.length);
866 
867  for (var i = 0; i < _segments.length; i++)
868  [self setWidth:[_segments[i] width] + remainingWidth forSegment:i];
869 
870  [self tileWithChangedSegment:0];
871  }
872 
873  return self;
874 }
875 
876 - (void)encodeWithCoder:(CPCoder)aCoder
877 {
878  [super encodeWithCoder:aCoder];
879 
880  [aCoder encodeObject:_segments forKey:CPSegmentedControlSegmentsKey];
881  [aCoder encodeInt:_selectedSegment forKey:CPSegmentedControlSelectedKey];
882  [aCoder encodeInt:_segmentStyle forKey:CPSegmentedControlSegmentStyleKey];
883  [aCoder encodeInt:_trackingMode forKey:CPSegmentedControlTrackingModeKey];
884 }
885 
886 @end
887 
888 
889 @implementation _CPSegmentItem : CPObject
890 {
891  CPImage image;
894  BOOL selected;
895  BOOL enabled;
896  int tag;
897  int width;
898 
899  CGRect frame;
900 }
901 
902 - (id)init
903 {
904  if (self = [super init])
905  {
906  image = nil;
907  label = @"";
908  menu = nil;
909  selected = NO;
910  enabled = YES;
911  tag = -1;
912  width = 0;
913 
914  frame = CGRectMakeZero();
915  }
916  return self;
917 }
918 
919 @end
920 
921 var CPSegmentItemImageKey = "CPSegmentItemImageKey",
922  CPSegmentItemLabelKey = "CPSegmentItemLabelKey",
923  CPSegmentItemMenuKey = "CPSegmentItemMenuKey",
924  CPSegmentItemSelectedKey = "CPSegmentItemSelectedKey",
925  CPSegmentItemEnabledKey = "CPSegmentItemEnabledKey",
926  CPSegmentItemTagKey = "CPSegmentItemTagKey",
927  CPSegmentItemWidthKey = "CPSegmentItemWidthKey";
928 
929 @implementation _CPSegmentItem (CPCoding)
930 
931 - (id)initWithCoder:(CPCoder)aCoder
932 {
933  self = [super init];
934 
935  if (self)
936  {
937  image = [aCoder decodeObjectForKey:CPSegmentItemImageKey];
938  label = [aCoder decodeObjectForKey:CPSegmentItemLabelKey];
939  menu = [aCoder decodeObjectForKey:CPSegmentItemMenuKey];
940  selected = [aCoder decodeBoolForKey:CPSegmentItemSelectedKey];
941  enabled = [aCoder decodeBoolForKey:CPSegmentItemEnabledKey];
942  tag = [aCoder decodeIntForKey:CPSegmentItemTagKey];
943  width = [aCoder decodeFloatForKey:CPSegmentItemWidthKey];
944 
945  frame = CGRectMakeZero();
946  }
947 
948  return self;
949 }
950 
951 - (void)encodeWithCoder:(CPCoder)aCoder
952 {
953  [aCoder encodeObject:image forKey:CPSegmentItemImageKey];
954  [aCoder encodeObject:label forKey:CPSegmentItemLabelKey];
955  [aCoder encodeObject:menu forKey:CPSegmentItemMenuKey];
956  [aCoder encodeBool:selected forKey:CPSegmentItemSelectedKey];
957  [aCoder encodeBool:enabled forKey:CPSegmentItemEnabledKey];
958  [aCoder encodeInt:tag forKey:CPSegmentItemTagKey];
959  [aCoder encodeFloat:width forKey:CPSegmentItemWidthKey];
960 }
961 
962 @end