+ } else if ([ctl isKindOfClass:[UILabel class]]) {
+ UILabel *t = (UILabel *) ctl;
+ CGRect r = t.frame;
+ r.size.width = 250; // WTF! Black magic!
+ r.size.width -= LEFT_MARGIN;
+ [t setFrame:r];
+ [t sizeToFit];
+ r = t.frame;
+ h = r.size.height + LINE_SPACING * 3;
+# ifdef USE_HTML_LABELS
+
+ } else if ([ctl isKindOfClass:[HTMLLabel class]]) {
+
+ HTMLLabel *t = (HTMLLabel *) ctl;
+ CGRect r = t.frame;
+ r.size.width = [tv frame].size.width;
+ r.size.width -= LEFT_MARGIN * 2;
+ [t setFrame:r];
+ [t sizeToFit];
+ r = t.frame;
+ h = r.size.height + LINE_SPACING * 3;
+
+# endif // USE_HTML_LABELS
+ } else {
+ CGFloat h2 = [ctl frame].size.height;
+ h2 += LINE_SPACING * 2;
+ if (h2 > h) h = h2;
+ }
+
+ return h;
+}
+
+
+- (void)refreshTableView
+{
+ UITableView *tv = (UITableView *) [self view];
+ NSMutableArray *a = [NSMutableArray arrayWithCapacity:20];
+ int rows = [self numberOfSectionsInTableView:tv];
+ for (int i = 0; i < rows; i++) {
+ int cols = [self tableView:tv numberOfRowsInSection:i];
+ for (int j = 0; j < cols; j++) {
+ NSUInteger ip[2];
+ ip[0] = i;
+ ip[1] = j;
+ [a addObject: [NSIndexPath indexPathWithIndexes:ip length:2]];
+ }
+ }
+
+ [tv beginUpdates];
+ [tv reloadRowsAtIndexPaths:a withRowAnimation:UITableViewRowAnimationNone];
+ [tv endUpdates];
+}
+
+
+- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)o
+{
+ [NSTimer scheduledTimerWithTimeInterval: 0
+ target:self
+ selector:@selector(refreshTableView)
+ userInfo:nil
+ repeats:NO];
+}
+
+
+#ifndef USE_PICKER_VIEW
+
+- (void)updateRadioGroupCell:(UITableViewCell *)cell
+ button:(RadioButton *)b
+{
+ NSArray *item = [[b items] objectAtIndex: [b index]];
+ NSString *pref_key = [item objectAtIndex:1];
+ NSObject *pref_val = [item objectAtIndex:2];
+ NSObject *current = [userDefaultsController objectForKey:pref_key];
+
+ // Convert them both to strings and compare those, so that
+ // we don't get screwed by int 1 versus string "1".
+ // Will boolean true/1 screw us here too?
+ //
+ NSString *pref_str = ([pref_val isKindOfClass:[NSString class]]
+ ? (NSString *) pref_val
+ : [(NSNumber *) pref_val stringValue]);
+ NSString *current_str = ([current isKindOfClass:[NSString class]]
+ ? (NSString *) current
+ : [(NSNumber *) current stringValue]);
+ BOOL match_p = [current_str isEqualToString:pref_str];
+
+ // NSLog(@"\"%@\" = \"%@\" | \"%@\" ", pref_key, pref_val, current_str);
+
+ if (match_p)
+ [cell setAccessoryType:UITableViewCellAccessoryCheckmark];
+ else
+ [cell setAccessoryType:UITableViewCellAccessoryNone];
+}
+
+
+- (void)tableView:(UITableView *)tv
+ didSelectRowAtIndexPath:(NSIndexPath *)ip
+{
+ RadioButton *ctl = [[controls objectAtIndex:[ip indexAtPosition:0]]
+ objectAtIndex:[ip indexAtPosition:1]];
+ if (! [ctl isKindOfClass:[RadioButton class]])
+ return;
+
+ [self radioAction:ctl];
+ [self refreshTableView];
+}
+
+
+#endif // !USE_PICKER_VIEW
+
+
+
+- (UITableViewCell *)tableView:(UITableView *)tv
+ cellForRowAtIndexPath:(NSIndexPath *)ip
+{
+#if 0
+ /* #### If we re-use cells, then clicking on a checkbox RadioButton
+ (in non-USE_PICKER_VIEW mode) makes all the cells disappear.
+ This doesn't happen if we don't re-use any cells. Oh well.
+ */
+ NSString *id = [NSString stringWithFormat: @"%d:%d",
+ [ip indexAtPosition:0],
+ [ip indexAtPosition:1]];
+ UITableViewCell *cell = [tv dequeueReusableCellWithIdentifier: id];
+
+ if (cell) return cell;
+#else
+ NSString *id = nil;
+ UITableViewCell *cell;
+#endif
+
+ cell = [[[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault
+ reuseIdentifier: id]
+ autorelease];
+ cell.selectionStyle = UITableViewCellSelectionStyleNone;
+
+ CGRect p = [cell frame];
+ CGRect r;
+
+ p.size.height = [self tableView:tv heightForRowAtIndexPath:ip];
+ [cell setFrame:p];
+
+ // Allocate more space to the controls on iPad screens,
+ // and on landscape-mode iPhones.
+ CGFloat ww = [tv frame].size.width;
+ CGFloat left_edge = (ww > 700
+ ? p.size.width * 0.9
+ : ww > 320
+ ? p.size.width * 0.5
+ : p.size.width * 0.3);
+ CGFloat right_edge = p.origin.x + p.size.width - LEFT_MARGIN;
+
+
+ NSView *ctl = [[controls objectAtIndex:[ip indexAtPosition:0]]
+ objectAtIndex:[ip indexAtPosition:1]];
+
+ if ([ctl isKindOfClass:[NSArray class]]) {
+ // This cell has a set of objects in it.
+ NSArray *set = (NSArray *) ctl;
+ switch ([set count]) {
+ case 2:
+ {
+ // With 2 elements, the first of the pair must be a label.
+ UILabel *label = (UILabel *) [set objectAtIndex: 0];
+ NSAssert ([label isKindOfClass:[UILabel class]], @"unhandled type");
+ ctl = [set objectAtIndex: 1];
+
+ r = [ctl frame];
+ if ([ctl isKindOfClass:[UISwitch class]]) {
+ // Flush right checkboxes.
+ ctl.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
+ r.size.width = 80; // Magic.
+ r.origin.x = right_edge - r.size.width;
+ } else {
+ // Expandable sliders.
+ ctl.autoresizingMask = UIViewAutoresizingFlexibleWidth;
+ r.origin.x = left_edge;
+ r.size.width = right_edge - r.origin.x;
+ }
+ r.origin.y = (p.size.height - r.size.height) / 2;
+ [ctl setFrame:r];
+
+ // Make a box.
+ NSView *box = [[UIView alloc] initWithFrame:p];
+ [box addSubview: ctl];
+
+ // cell.textLabel.text = [(UILabel *) ctl text];
+ r = [label frame];
+ r.origin.x = LEFT_MARGIN;
+ r.origin.y = 0;
+ r.size.width = [ctl frame].origin.x - r.origin.x;
+ r.size.height = p.size.height;
+ [label setFrame:r];
+ [label setFont:[NSFont boldSystemFontOfSize: FONT_SIZE]];
+ label.autoresizingMask = UIViewAutoresizingFlexibleRightMargin;
+ box. autoresizingMask = UIViewAutoresizingFlexibleWidth;
+ [box addSubview: label];
+
+ ctl = box;
+ }
+ break;
+ case 3:
+ case 4:
+ {
+ // With 3 elements, the first and last must be labels.
+ // With 4 elements, the first, second and last must be labels.
+ int i = 0;
+ UILabel *top = ([set count] == 4
+ ? [set objectAtIndex: i++]
+ : 0);
+ UILabel *left = [set objectAtIndex: i++];
+ NSView *mid = [set objectAtIndex: i++];
+ UILabel *right = [set objectAtIndex: i++];
+ NSAssert (!top || [top isKindOfClass:[UILabel class]], @"WTF");
+ NSAssert ( [left isKindOfClass:[UILabel class]], @"WTF");
+ NSAssert ( ![mid isKindOfClass:[UILabel class]], @"WTF");
+ NSAssert ( [right isKindOfClass:[UILabel class]], @"WTF");
+
+ // 3 elements: control at top of cell.
+ // 4 elements: center the control vertically.
+ r = [mid frame];
+# ifdef LABEL_ABOVE_SLIDER
+ left_edge = LEFT_MARGIN;
+# endif
+ r.origin.x = left_edge;
+ r.size.width = right_edge - r.origin.x;
+ r.origin.y = ([set count] == 3
+ ? 8
+ : (p.size.height - r.size.height) / 2);
+ [mid setFrame:r];
+
+ // Top label goes above, flush center/top.
+ if (top) {
+ r.size = [[top text] sizeWithFont:[top font]
+ constrainedToSize:
+ CGSizeMake (p.size.width - LEFT_MARGIN*2,
+ 100000)
+ lineBreakMode:[top lineBreakMode]];
+ r.origin.x = (p.size.width - r.size.width) / 2;
+ r.origin.y = 4;
+ [top setFrame:r];
+ }
+
+ // Left label goes under control, flush left/bottom.
+ r.size = [[left text] sizeWithFont:[left font]
+ constrainedToSize:
+ CGSizeMake(p.size.width - LEFT_MARGIN*2,
+ 100000)
+ lineBreakMode:[left lineBreakMode]];
+ r.origin.x = [mid frame].origin.x;
+ r.origin.y = p.size.height - r.size.height - 4;
+ [left setFrame:r];
+
+ // Right label goes under control, flush right/bottom.
+ r = [right frame];
+ r.size = [[right text] sizeWithFont:[right font]
+ constrainedToSize:
+ CGSizeMake(p.size.width - LEFT_MARGIN*2,
+ 1000000)
+ lineBreakMode:[right lineBreakMode]];
+ r.origin.x = ([mid frame].origin.x + [mid frame].size.width -
+ r.size.width);
+ r.origin.y = [left frame].origin.y;
+ [right setFrame:r];
+
+ // Then make a box.
+ ctl = [[UIView alloc] initWithFrame:p];
+ if (top) {
+# ifdef LABEL_ABOVE_SLIDER
+ [ctl addSubview: top];
+ top.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin|
+ UIViewAutoresizingFlexibleRightMargin);
+# else
+ r = [top frame];
+ r.origin.x = LEFT_MARGIN;
+ r.origin.y = 0;
+ r.size.width = [mid frame].origin.x - r.origin.x;
+ r.size.height = p.size.height;
+ [top setFrame:r];
+ top.autoresizingMask = UIViewAutoresizingFlexibleRightMargin;
+ [ctl addSubview: top];
+# endif
+ }
+ [ctl addSubview: left];
+ [ctl addSubview: mid];
+ [ctl addSubview: right];
+
+ left. autoresizingMask = UIViewAutoresizingFlexibleRightMargin;
+ mid. autoresizingMask = UIViewAutoresizingFlexibleWidth;
+ right.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
+ ctl. autoresizingMask = UIViewAutoresizingFlexibleWidth;
+ }
+ break;
+ default:
+ NSAssert (0, @"unhandled size");
+ }
+ } else {
+ // A single view, not a pair.
+
+ r = [ctl frame];
+ r.origin.x = LEFT_MARGIN;
+ r.origin.y = LINE_SPACING;
+ r.size.width = right_edge - r.origin.x;
+ [ctl setFrame:r];
+
+ ctl.autoresizingMask = UIViewAutoresizingFlexibleWidth;
+
+# ifndef USE_PICKER_VIEW
+ if ([ctl isKindOfClass:[RadioButton class]])
+ [self updateRadioGroupCell:cell button:(RadioButton *)ctl];
+# endif // USE_PICKER_VIEW
+ }
+
+ if ([ctl isKindOfClass:[UILabel class]]) {
+ // Make label full height to allow text to line-wrap if necessary.
+ r = [ctl frame];
+ r.origin.y = p.origin.y;
+ r.size.height = p.size.height;
+ [ctl setFrame:r];
+ }
+
+ [cell.contentView addSubview: ctl];
+
+ return cell;
+}
+# endif // USE_IPHONE
+
+
+/* When this object is instantiated, it parses the XML file and creates
+ controls on itself that are hooked up to the appropriate preferences.
+ The default size of the view is just big enough to hold them all.
+ */
+- (id)initWithXML: (NSData *) xml_data
+ options: (const XrmOptionDescRec *) _opts
+ controller: (NSUserDefaultsController *) _prefs
+ defaults: (NSDictionary *) _defs
+{
+# ifndef USE_IPHONE
+ self = [super init];
+# else // !USE_IPHONE
+ self = [super initWithStyle:UITableViewStyleGrouped];
+ self.title = [saver_name stringByAppendingString:@" Settings"];
+# endif // !USE_IPHONE
+ if (! self) return 0;
+
+ // instance variables
+ opts = _opts;
+ defaultOptions = _defs;
+ userDefaultsController = _prefs;
+ [userDefaultsController retain];
+
+ NSXMLParser *xmlDoc = [[NSXMLParser alloc] initWithData:xml_data];
+
+ if (!xmlDoc) {
+ NSAssert1 (0, @"XML Error: %@", xml_data);
+ return nil;
+ }
+ [xmlDoc setDelegate:self];
+ if (! [xmlDoc parse]) {
+ NSError *err = [xmlDoc parserError];
+ NSAssert2 (0, @"XML Error: %@: %@", xml_data, err);