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