API  0.9.8
 All Classes Files Functions Variables Typedefs Macros Groups Pages
CPRadio.j
Go to the documentation of this file.
1 /*
2  * CPRadio.j
3  * AppKit
4  *
5  * Created by Francisco Tolmasky.
6  * Copyright 2009, 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 
26 @global CPApp
27 
28 
68 
69 @implementation CPRadio : CPButton
70 {
71  CPRadioGroup _radioGroup;
72 }
73 
74 + (id)radioWithTitle:(CPString)aTitle theme:(CPTheme)aTheme
75 {
76  return [self buttonWithTitle:aTitle theme:aTheme];
77 }
78 
79 + (id)radioWithTitle:(CPString)aTitle
80 {
81  return [self buttonWithTitle:aTitle];
82 }
83 
84 + (CPButton)standardButtonWithTitle:(CPString)aTitle
85 {
86  var button = [[CPRadio alloc] init];
87 
88  [button setTitle:aTitle];
89 
90  return button;
91 }
92 
93 + (CPString)defaultThemeClass
94 {
95  return @"radio";
96 }
97 
98 // Designated Initializer
99 - (id)initWithFrame:(CGRect)aFrame radioGroup:(CPRadioGroup)aRadioGroup
100 {
101  self = [super initWithFrame:aFrame];
102 
103  if (self)
104  {
105  [self setRadioGroup:aRadioGroup];
106 
107  [self setHighlightsBy:CPContentsCellMask];
108  [self setShowsStateBy:CPContentsCellMask];
109 
110  // Defaults?
111  [self setImagePosition:CPImageLeft];
112  [self setAlignment:CPLeftTextAlignment];
113 
114  [self setBordered:YES];
115  }
116 
117  return self;
118 }
119 
120 - (id)initWithFrame:(CGRect)aFrame
121 {
122  return [self initWithFrame:aFrame radioGroup:[CPRadioGroup new]];
123 }
124 
125 - (CPInteger)nextState
126 {
127  return CPOnState;
128 }
129 
130 - (void)setRadioGroup:(CPRadioGroup)aRadioGroup
131 {
132  if (_radioGroup === aRadioGroup)
133  return;
134 
135  [_radioGroup _removeRadio:self];
136  _radioGroup = aRadioGroup;
137  [_radioGroup _addRadio:self];
138 }
139 
140 - (CPRadioGroup)radioGroup
141 {
142  return _radioGroup;
143 }
144 
145 - (void)setObjectValue:(id)aValue
146 {
147  [super setObjectValue:aValue];
148 
149  if ([self state] === CPOnState)
150  [_radioGroup _setSelectedRadio:self];
151 }
152 
153 - (BOOL)sendAction:(SEL)anAction to:(id)anObject
154 {
155  [super sendAction:anAction to:anObject];
156 
157  if (_radioGroup)
158  [CPApp sendAction:[_radioGroup action] to:[_radioGroup target] from:_radioGroup];
159 }
160 
161 @end
162 
163 var CPRadioRadioGroupKey = @"CPRadioRadioGroupKey";
164 
165 @implementation CPRadio (CPCoding)
166 
167 - (id)initWithCoder:(CPCoder)aCoder
168 {
169  self = [super initWithCoder:aCoder];
170 
171  if (self)
172  _radioGroup = [aCoder decodeObjectForKey:CPRadioRadioGroupKey];
173 
174  return self;
175 }
176 
177 - (void)encodeWithCoder:(CPCoder)aCoder
178 {
179  [super encodeWithCoder:aCoder];
180 
181  [aCoder encodeObject:_radioGroup forKey:CPRadioRadioGroupKey];
182 }
183 
184 - (CPImage)image
185 {
186  return [self currentValueForThemeAttribute:@"image"];
187 }
188 
189 - (CPImage)alternateImage
190 {
191  return [self currentValueForThemeAttribute:@"image"];
192 }
193 
194 - (BOOL)startTrackingAt:(CGPoint)aPoint
195 {
196  var startedTracking = [super startTrackingAt:aPoint];
197  [self highlight:YES];
198  return startedTracking;
199 }
200 
201 @end
202 
203 @implementation CPRadioGroup : CPObject
204 {
205  CPArray _radios;
206  CPRadio _selectedRadio;
207 
208  BOOL _enabled;
209  BOOL _hidden;
210 
211  id _target;
212  SEL _action;
213 }
214 
215 + (void)initialize
216 {
217  if (self !== [CPRadioGroup class])
218  return;
219 
220  [self exposeBinding:CPSelectedValueBinding];
221  [self exposeBinding:CPSelectedTagBinding];
222  [self exposeBinding:CPSelectedIndexBinding];
223 
224  [self exposeBinding:CPEnabledBinding];
225  [self exposeBinding:CPHiddenBinding];
226 }
227 
228 - (id)init
229 {
230  self = [super init];
231 
232  if (self)
233  {
234  _radios = [];
235  _selectedRadio = nil;
236  _enabled = YES;
237  _hidden = NO;
238  }
239 
240  return self;
241 }
242 
247 - (void)selectRadioAtIndex:(int)index
248 {
249  if (index === -1)
250  [self _setSelectedRadio:nil];
251  else
252  {
253  var radio = [_radios objectAtIndex:index];
254 
255  [self _setSelectedRadio:radio];
256  [radio setState:CPOnState];
257  }
258 }
259 
265 - (BOOL)selectRadioWithTag:(int)tag
266 {
267  var index = [_radios indexOfObjectPassingTest:function(radio)
268  {
269  return [radio tag] === tag;
270  }];
271 
272  if (index !== CPNotFound)
273  {
274  [self selectRadioAtIndex:index];
275  return YES;
276  }
277  else
278  return NO;
279 }
280 
285 - (CPRadio)selectedRadio
286 {
287  return _selectedRadio;
288 }
289 
295 - (int)selectedRadioIndex
296 {
297  return [_radios indexOfObject:_selectedRadio];
298 }
299 
300 - (CPArray)radios
301 {
302  return _radios;
303 }
304 
305 - (void)setEnabled:(BOOL)enabled
306 {
307  [_radios makeObjectsPerformSelector:@selector(setEnabled:) withObject:enabled];
308 }
309 
310 - (void)setHidden:(BOOL)hidden
311 {
312  [_radios makeObjectsPerformSelector:@selector(setHidden:) withObject:hidden];
313 }
314 
315 #pragma mark Private
316 
317 - (void)_addRadio:(CPRadio)aRadio
318 {
319  if ([_radios indexOfObject:aRadio] === CPNotFound)
320  [_radios addObject:aRadio];
321 
322  if ([aRadio state] === CPOnState)
323  [self _setSelectedRadio:aRadio];
324 }
325 
326 - (void)_removeRadio:(CPRadio)aRadio
327 {
328  if (_selectedRadio === aRadio)
329  _selectedRadio = nil;
330 
331  [_radios removeObject:aRadio];
332 }
333 
339 - (void)_selectRadioWithTitle:(CPString)aTitle
340 {
341  var index = [_radios indexOfObjectPassingTest:function(radio)
342  {
343  return [radio title] === aTitle;
344  }];
345 
346  [self selectRadioAtIndex:index];
347 }
348 
349 - (void)_setSelectedRadio:(CPRadio)aRadio
350 {
351  if (_selectedRadio === aRadio)
352  return;
353 
354  [_selectedRadio setState:CPOffState];
355 
356  _selectedRadio = aRadio;
357  [_CPRadioGroupSelectionBinder _reverseSetValueFromExclusiveBinderForObject:self];
358 }
359 
360 @end
361 
362 var CPRadioGroupRadiosKey = @"CPRadioGroupRadiosKey",
363  CPRadioGroupSelectedRadioKey = @"CPRadioGroupSelectedRadioKey";
364 
365 @implementation CPRadioGroup (CPCoding)
366 
367 - (id)initWithCoder:(CPCoder)aCoder
368 {
369  self = [super init];
370 
371  if (self)
372  {
373  _radios = [aCoder decodeObjectForKey:CPRadioGroupRadiosKey];
374  _selectedRadio = [aCoder decodeObjectForKey:CPRadioGroupSelectedRadioKey];
375  }
376 
377  return self;
378 }
379 
380 - (void)encodeWithCoder:(CPCoder)aCoder
381 {
382  [aCoder encodeObject:_radios forKey:CPRadioGroupRadiosKey];
383  [aCoder encodeObject:_selectedRadio forKey:CPRadioGroupSelectedRadioKey];
384 }
385 
386 @end
387 
389 
390 + (Class)_binderClassForBinding:(CPString)aBinding
391 {
392  if (aBinding === CPSelectedValueBinding ||
393  aBinding === CPSelectedTagBinding ||
394  aBinding === CPSelectedIndexBinding)
395  {
396  var capitalizedBinding = aBinding.charAt(0).toUpperCase() + aBinding.substr(1);
397 
398  return [CPClassFromString(@"_CPRadioGroup" + capitalizedBinding + "Binder") class];
399  }
400  else if ([aBinding hasPrefix:CPEnabledBinding])
401  return [CPMultipleValueAndBinding class];
402  else if ([aBinding hasPrefix:CPHiddenBinding])
403  return [CPMultipleValueOrBinding class];
404 
405  return [super _binderClassForBinding:aBinding];
406 }
407 
408 + (BOOL)isBindingExclusive:(CPString)aBinding
409 {
410  return (aBinding == CPSelectedIndexBinding ||
411  aBinding == CPSelectedTagBinding ||
412  aBinding == CPSelectedValueBinding);
413 }
414 
415 @end
416 @implementation _CPRadioGroupSelectionBinder : CPBinder
417 {
418  id __doxygen__;
419 }
420 
421 - (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding
422 {
423  [self setValue:aValue forBinding:aBinding];
424 }
425 
426 @end
427 @implementation _CPRadioGroupSelectedIndexBinder : _CPRadioGroupSelectionBinder
428 {
429  id __doxygen__;
430 }
431 
432 - (void)setValue:(id)aValue forBinding:(CPString)aBinding
433 {
434  [_source selectRadioAtIndex:aValue];
435 }
436 
437 - (id)valueForBinding:(CPString)aBinding
438 {
439  return [_source selectedRadioIndex];
440 }
441 
442 @end
443 @implementation _CPRadioGroupSelectedTagBinder : _CPRadioGroupSelectionBinder
444 {
445  id __doxygen__;
446 }
447 
448 - (void)setValue:(id)aValue forBinding:(CPString)aBinding
449 {
450  [_source selectRadioWithTag:aValue];
451 }
452 
453 - (id)valueForBinding:(CPString)aBinding
454 {
455  return [[_source selectedRadio] tag];
456 }
457 
458 @end
459 @implementation _CPRadioGroupSelectedValueBinder : _CPRadioGroupSelectionBinder
460 {
461  id __doxygen__;
462 }
463 
464 - (void)setValue:(id)aValue forBinding:(CPString)aBinding
465 {
466  [_source _selectRadioWithTitle:aValue];
467 }
468 
469 - (id)valueForBinding:(CPString)aBinding
470 {
471  return [[_source selectedRadio] title];
472 }
473 
474 @end
475 
477 
481 - (BOOL)enabled
482 {
483  return _enabled;
484 }
485 
489 - (BOOL)hidden
490 {
491  return _hidden;
492 }
493 
497 - (id)target
498 {
499  return _target;
500 }
501 
505 - (void)setTarget:(id)aValue
506 {
507  _target = aValue;
508 }
509 
513 - (SEL)action
514 {
515  return _action;
516 }
517 
521 - (void)setAction:(SEL)aValue
522 {
523  _action = aValue;
524 }
525 
526 @end