605d6e2dea70da61c3c1004243a5fa1caca82528
[xscreensaver] / OSX / InvertedSlider.m
1 /* xscreensaver, Copyright (c) 2006-2013 Jamie Zawinski <jwz@jwz.org>
2 *
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation.  No representations are made about the suitability of this
8 * software for any purpose.  It is provided "as is" without express or 
9 * implied warranty.
10 *
11 * This is a subclass of NSSlider that is flipped horizontally:
12 * the high value is on the left and the low value is on the right.
13 */
14
15 #import "InvertedSlider.h"
16
17 @implementation InvertedSlider
18
19 - (id) initWithFrame:(NSRect)r
20 {
21   self = [super initWithFrame:r];
22   if (! self) return 0;
23   inverted = YES;
24   integers = NO;
25   return self;
26 }
27
28 - (id) initWithFrame:(NSRect)r inverted:(BOOL)_inv integers:(BOOL)_int
29 {
30   self = [self initWithFrame:r];
31   inverted = _inv;
32   integers = _int;
33   return self;
34 }
35
36
37 -(double) transformValue:(double) value
38 {
39   double v2 = (integers
40                ? (int) (value + (value < 0 ? -0.5 : 0.5))
41                : value);
42   double low   = [self minValue];
43   double high  = [self maxValue];
44   double range = high - low;
45   double off   = v2 - low;
46   if (inverted)
47     v2 = low + (range - off);
48   // NSLog (@" ... %.1f -> %.1f    [%.1f - %.1f]", value, v2, low, high);
49   return v2;
50 }
51
52 #ifndef USE_IPHONE
53
54 /* On MacOS, we have to transform the value on every entry and exit point
55    to this class.  So, we implement doubleValue and setDoubleValue to
56    transform the value; and we then have to re-implement every getter and
57    setter in terms of those.  There's no way to simply change how the
58    slider is displayed without mucking with the value inside of it.
59  */
60
61 -(double) doubleValue
62 {
63   return [self transformValue:[super doubleValue]];
64 }
65
66 -(void) setDoubleValue:(double)v
67 {
68   return [super setDoubleValue:[self transformValue:v]];
69 }
70
71 -(float)floatValue       { return (float) [self doubleValue]; }
72 -(int)intValue           { return (int) [self doubleValue]; }
73 -(NSInteger)integerValue { return (NSInteger) [self doubleValue]; }
74 -(id)objectValue { return [NSNumber numberWithDouble:[self doubleValue]]; }
75
76 -(NSString *)stringValue
77 {
78   if (integers)
79     return [NSString stringWithFormat:@"%d", [self intValue]];
80   else
81     return [NSString stringWithFormat:@"%f", [self doubleValue]];
82 }
83
84 - (NSAttributedString *)attributedStringValue;
85 {
86   // #### "Build and Analyze" says this leaks. Unsure whether this is true.
87   return [[NSAttributedString alloc] initWithString:[self stringValue]];
88 }
89
90 -(void)setFloatValue:(float)v       { [self setDoubleValue: (double) v];      }
91 -(void)setIntValue:  (int)v         { [self setDoubleValue: (double) v];      }
92 -(void)setIntegerValue:(NSInteger)v { [self setDoubleValue: (double) v];      }
93 -(void)setStringValue:(NSString *)v { [self setDoubleValue: [v doubleValue]]; }
94 -(void)takeIntValueFrom:(id)f       { [self setIntValue:    [f intValue]];    }
95 -(void)takeFloatValueFrom:(id)f     { [self setFloatValue:  [f floatValue]];  }
96 -(void)takeDoubleValueFrom:(id)f    { [self setDoubleValue: [f doubleValue]]; }
97 -(void)takeStringValueFrom:(id)f    { [self setStringValue: [f stringValue]]; }
98 -(void)takeObjectValueFrom:(id)f    { [self setObjectValue: [f objectValue]]; }
99 -(void)takeIntegerValueFrom:(id)f   { [self setIntegerValue:[f integerValue]];}
100 -(void) setAttributedStringValue:(NSAttributedString *)v {
101   [self setStringValue:[v string]];
102 }
103
104 -(void) setObjectValue:(id <NSCopying>)v
105 {
106   NSAssert2((v == nil) ||
107             [(NSObject *) v respondsToSelector:@selector(doubleValue)], 
108             @"argument %@ to %s does not respond to doubleValue",
109             v, __PRETTY_FUNCTION__);
110   [self setDoubleValue:[((NSNumber *) v) doubleValue]];
111 }
112
113 #else  // USE_IPHONE
114
115 /* On iOS, we have control over how the value is displayed, but there's no
116    way to transform the value on input and output: if we wrap 'value' and
117    'setValue' analagously to what we do on MacOS, things fail in weird
118    ways.  Presumably some parts of the system are accessing the value
119    instance variable directly instead of going through the methods.
120
121    So the only way around this is to enforce that all of our calls into
122    this object use a new API: 'transformedValue' and 'setTransformedValue'.
123    The code in XScreenSaverConfigSheet uses that instead.
124  */
125
126 - (CGRect)thumbRectForBounds:(CGRect)bounds
127                    trackRect:(CGRect)rect
128                        value:(float)value
129 {
130   CGRect thumb = [super thumbRectForBounds: bounds
131                                  trackRect: rect 
132                                      value: [self transformValue:value]];
133   if (inverted)
134     thumb.origin.x = rect.size.width - thumb.origin.x - thumb.size.width;
135   return thumb;
136 }
137
138 -(double) transformedValue
139 {
140   return [self transformValue: [self value]];
141 }
142
143 -(void) setTransformedValue:(double)v
144 {
145   [self setValue: [self transformValue: v]];
146 }
147
148 #endif // USE_IPHONE
149
150
151 @end