-/* xscreensaver, Copyright (c) 2006-2013 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 2006-2017 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
*/
#import "XScreenSaverConfigSheet.h"
+#import "Updater.h"
#import "jwxyz.h"
#import "InvertedSlider.h"
[n setName:key];
[n setObjectValue:val];
[attributes addObject:n];
+ [n release];
}
}
@end
+#pragma mark textMode value transformer
+
+// A value transformer for mapping "url" to "3" and vice versa in the
+// "textMode" preference, since NSMatrix uses NSInteger selectedIndex.
+
+#ifndef USE_IPHONE
+@interface TextModeTransformer: NSValueTransformer {}
+@end
+@implementation TextModeTransformer
++ (Class)transformedValueClass { return [NSString class]; }
++ (BOOL)allowsReverseTransformation { return YES; }
+
+- (id)transformedValue:(id)value {
+ if ([value isKindOfClass:[NSString class]]) {
+ int i = -1;
+ if ([value isEqualToString:@"date"]) { i = 0; }
+ else if ([value isEqualToString:@"literal"]) { i = 1; }
+ else if ([value isEqualToString:@"file"]) { i = 2; }
+ else if ([value isEqualToString:@"url"]) { i = 3; }
+ else if ([value isEqualToString:@"program"]) { i = 4; }
+ if (i != -1)
+ value = [NSNumber numberWithInt: i];
+ }
+ return value;
+}
+
+- (id)reverseTransformedValue:(id)value {
+ if ([value isKindOfClass:[NSNumber class]]) {
+ switch ((int) [value doubleValue]) {
+ case 0: value = @"date"; break;
+ case 1: value = @"literal"; break;
+ case 2: value = @"file"; break;
+ case 3: value = @"url"; break;
+ case 4: value = @"program"; break;
+ }
+ }
+ return value;
+}
+@end
+#endif // USE_IPHONE
+
+
#pragma mark Implementing radio buttons
/* The UIPickerView is a hideous and uncustomizable piece of shit.
webView = [[UIWebView alloc] init];
webView.delegate = self;
webView.dataDetectorTypes = UIDataDetectorTypeNone;
- self. autoresizingMask = (UIViewAutoresizingFlexibleWidth |
- UIViewAutoresizingFlexibleHeight);
- webView.autoresizingMask = (UIViewAutoresizingFlexibleWidth |
- UIViewAutoresizingFlexibleHeight);
+ self. autoresizingMask = UIViewAutoresizingNone; // we do it manually
+ webView.autoresizingMask = UIViewAutoresizingNone;
+ webView.scrollView.scrollEnabled = NO;
+ webView.scrollView.bounces = NO;
+ webView.opaque = NO;
+ [webView setBackgroundColor:[UIColor clearColor]];
+
[self addSubview: webView];
[self setHTML: h];
return self;
" font-family: \"%@\";"
" font-size: %.4fpx;" // Must be "px", not "pt"!
" line-height: %.4fpx;" // And no spaces before it.
+ " -webkit-text-size-adjust: none;"
"}"
"\n//-->\n"
"</STYLE>"
[font pointSize],
[font lineHeight],
h];
+ [webView stopLoading];
[webView loadHTMLString:h2 baseURL:[NSURL URLWithString:@""]];
}
- (void) setText: (NSString *)t
{
+ t = [t stringByTrimmingCharactersInSet:[NSCharacterSet
+ whitespaceCharacterSet]];
t = [t stringByReplacingOccurrencesOfString:@"&" withString:@"&"];
t = [t stringByReplacingOccurrencesOfString:@"<" withString:@"<"];
t = [t stringByReplacingOccurrencesOfString:@">" withString:@">"];
}
h = [NSString stringWithFormat: @"%@ %@", h, s];
}
+
+ h = [h stringByReplacingOccurrencesOfString:@" <P> " withString:@"<P>"];
+ h = [h stringByReplacingOccurrencesOfString:@"<BR><P>" withString:@"<P>"];
+ h = [h stringByTrimmingCharactersInSet:[NSCharacterSet
+ whitespaceAndNewlineCharacterSet]];
+
[self setHTML: h];
}
r.origin.x = 0;
r.origin.y = 0;
[webView setFrame: r];
- [self setHTML: html];
- [webView reload];
}
{
NSString *result = @"";
+ // Add newlines.
str = [str stringByReplacingOccurrencesOfString:@"<P>"
withString:@"<BR><BR>"
options:NSCaseInsensitiveSearch
options:NSCaseInsensitiveSearch
range:NSMakeRange(0, [str length])];
+ // Remove HREFs.
for (NSString *s in [str componentsSeparatedByString: @"<"]) {
NSRange r = [s rangeOfString:@">"];
if (r.length > 0)
s = [s substringFromIndex: r.location + r.length];
result = [result stringByAppendingString: s];
}
+
+ // Compress internal horizontal whitespace.
+ str = result;
+ result = @"";
+ for (NSString *s in [str componentsSeparatedByCharactersInSet:
+ [NSCharacterSet whitespaceCharacterSet]]) {
+ if ([result length] == 0)
+ result = s;
+ else if ([s length] > 0)
+ result = [NSString stringWithFormat: @"%@ %@", result, s];
+ }
+
return result;
}
/* It would be sensible to just ask the UIWebView how tall the page is,
instead of hoping that NSString and UIWebView measure fonts and do
- wrapping in exactly the same way, but I can't make that work.
- Maybe because it loads async?
+ wrapping in exactly the same way, but since UIWebView is asynchronous,
+ we'd have to wait for the document to load first, e.g.:
+
+ - Start the document loading;
+ - return a default height to use for the UITableViewCell;
+ - wait for the webViewDidFinishLoad delegate method to fire;
+ - then force the UITableView to reload, to pick up the new height.
+
+ But I couldn't make that work.
*/
# if 0
r.size.height = [[webView
NSString *text = [self stripTags: html];
CGSize s = r.size;
s.height = 999999;
- s = [text sizeWithFont: font
- constrainedToSize: s
- lineBreakMode:NSLineBreakByWordWrapping];
-
- // GAAAH. Add one more line, or the UIWebView is still scrollable!
- // The text is sized right, but it lets you scroll it up anyway.
- s.height += [font pointSize];
-
+ s = [text boundingRectWithSize:s
+ options:NSStringDrawingUsesLineFragmentOrigin
+ attributes:@{NSFontAttributeName: font}
+ context:nil].size;
r.size.height = s.height;
# endif
instead, so transform keys to "SAVERNAME.KEY".
NOTE: This is duplicated in PrefsReader.m, cause I suck.
-*/
+ */
- (NSString *) makeKey:(NSString *)key
{
# ifdef USE_IPHONE
opts:(const XrmOptionDescRec *)opts_array
valRet:(NSString **)val_ret
{
- char buf[255];
+ char buf[1280];
char *tail = 0;
NSAssert(cmdline_switch, @"cmdline switch is null");
if (! [cmdline_switch getCString:buf maxLength:sizeof(buf)
}
+- (NSUserDefaultsController *)controllerForKey:(NSString *)key
+{
+ static NSDictionary *a = 0;
+ if (! a) {
+ a = UPDATER_DEFAULTS;
+ [a retain];
+ }
+ if ([a objectForKey:key])
+ // These preferences are global to all xscreensavers.
+ return globalDefaultsController;
+ else
+ // All other preferences are per-saver.
+ return userDefaultsController;
+}
+
+
#ifdef USE_IPHONE
// Called when a slider is bonked.
? [(InvertedSlider *) sender transformedValue]
: [sender value]);
- if (v == (int) v)
- [userDefaultsController setInteger:v forKey:pref_key];
- else
- [userDefaultsController setDouble:v forKey:pref_key];
+ [[self controllerForKey:pref_key]
+ setObject:((v == (int) v)
+ ? [NSNumber numberWithInt:(int) v]
+ : [NSNumber numberWithDouble: v])
+ forKey:pref_key];
}
// Called when a checkbox/switch is bonked.
[active_text_field resignFirstResponder];
NSString *pref_key = [pref_keys objectAtIndex: [sender tag]];
NSString *v = ([sender isOn] ? @"true" : @"false");
- [userDefaultsController setObject:v forKey:pref_key];
+ [[self controllerForKey:pref_key] setObject:v forKey:pref_key];
}
# ifdef USE_PICKER_VIEW
//NSString *label = [a objectAtIndex:0];
NSString *pref_key = [a objectAtIndex:1];
NSObject *pref_val = [a objectAtIndex:2];
- [userDefaultsController setObject:pref_val forKey:pref_key];
+ [[self controllerForKey:pref_key] setObject:pref_val forKey:pref_key];
}
# else // !USE_PICKER_VIEW
NSArray *item = [[sender items] objectAtIndex: [sender index]];
NSString *pref_key = [item objectAtIndex:1];
NSObject *pref_val = [item objectAtIndex:2];
- [userDefaultsController setObject:pref_val forKey:pref_key];
+ [[self controllerForKey:pref_key] setObject:pref_val forKey:pref_key];
}
- (BOOL)textFieldShouldBeginEditing:(UITextField *)tf
{
NSString *pref_key = [pref_keys objectAtIndex: [tf tag]];
NSString *txt = [tf text];
- [userDefaultsController setObject:txt forKey:pref_key];
+ [[self controllerForKey:pref_key] setObject:txt forKey:pref_key];
}
- (BOOL)textFieldShouldReturn:(UITextField *)tf
- (void) okAction:(NSObject *)arg
{
- [userDefaultsController commitEditing];
- [userDefaultsController save:self];
+ // Without the setAppliesImmediately:, when the saver restarts, it's still
+ // got the old settings. -[XScreenSaverConfigSheet traverseTree] sets this
+ // to NO; default is YES.
+
+ // #### However: I'm told that when these are set to YES, then changes to
+ // 'textLiteral', 'textURL' and 'textProgram' are ignored, but 'textFile'
+ // works. In StarWars, at least...
+
+ [userDefaultsController setAppliesImmediately:YES];
+ [globalDefaultsController setAppliesImmediately:YES];
+ [userDefaultsController commitEditing];
+ [globalDefaultsController commitEditing];
+ [userDefaultsController save:self];
+ [globalDefaultsController save:self];
[NSApp endSheet:self returnCode:NSOKButton];
[self close];
}
- (void) cancelAction:(NSObject *)arg
{
- [userDefaultsController revert:self];
+ [userDefaultsController revert:self];
+ [globalDefaultsController revert:self];
[NSApp endSheet:self returnCode:NSCancelButton];
[self close];
}
- (void) resetAction:(NSObject *)arg
{
# ifndef USE_IPHONE
- [userDefaultsController revertToInitialValues:self];
+ [userDefaultsController revertToInitialValues:self];
+ [globalDefaultsController revertToInitialValues:self];
# else // USE_IPHONE
for (NSString *key in defaultOptions) {
NSObject *val = [defaultOptions objectForKey:key];
- [userDefaultsController setObject:val forKey:key];
+ [[self controllerForKey:key] setObject:val forKey:key];
}
for (UIControl *ctl in pref_ctls) {
- (void) bindResource:(NSObject *)control key:(NSString *)pref_key
reload:(BOOL)reload_p
{
+ NSUserDefaultsController *prefs = [self controllerForKey:pref_key];
# ifndef USE_IPHONE
+ NSDictionary *opts_dict = nil;
NSString *bindto = ([control isKindOfClass:[NSPopUpButton class]]
? @"selectedObject"
: ([control isKindOfClass:[NSMatrix class]]
? @"selectedIndex"
: @"value"));
+
+ if ([control isKindOfClass:[NSMatrix class]]) {
+ opts_dict = @{ NSValueTransformerNameBindingOption:
+ @"TextModeTransformer" };
+ }
+
[control bind:bindto
- toObject:userDefaultsController
+ toObject:prefs
withKeyPath:[@"values." stringByAppendingString: pref_key]
- options:nil];
+ options:opts_dict];
+
# else // USE_IPHONE
SEL sel;
- NSObject *val = [userDefaultsController objectForKey:pref_key];
+ NSObject *val = [prefs objectForKey:pref_key];
NSString *sval = 0;
double dval = 0;
# endif // USE_IPHONE
# if 0
- NSObject *def = [[userDefaultsController defaults] objectForKey:pref_key];
+ NSObject *def = [[prefs defaults] objectForKey:pref_key];
NSString *s = [NSString stringWithFormat:@"bind: \"%@\"", pref_key];
s = [s stringByPaddingToLength:18 withString:@" " startingAtIndex:0];
- s = [NSString stringWithFormat:@"%@ = \"%@\"", s, def];
- s = [s stringByPaddingToLength:28 withString:@" " startingAtIndex:0];
- NSLog (@"%@ %@/%@", s, [def class], [control class]);
+ s = [NSString stringWithFormat:@"%@ = %@", s,
+ ([def isKindOfClass:[NSString class]]
+ ? [NSString stringWithFormat:@"\"%@\"", def]
+ : def)];
+ s = [s stringByPaddingToLength:30 withString:@" " startingAtIndex:0];
+ s = [NSString stringWithFormat:@"%@ %@ / %@", s,
+ [def class], [control class]];
+# ifndef USE_IPHONE
+ s = [NSString stringWithFormat:@"%@ / %@", s, bindto];
+# endif
+ NSLog (@"%@", s);
# endif
}
// Unwrap lines: delete \n but do not delete \n\n.
//
NSArray *lines = [text componentsSeparatedByString:@"\n"];
- int nlines = [lines count];
+ NSUInteger i, nlines = [lines count];
BOOL eolp = YES;
- int i;
text = @"\n"; // start with one blank line
eolp = YES;
} else if ([s characterAtIndex:0] == ' ' ||
[s hasPrefix:@"Copyright "] ||
+ [s hasPrefix:@"https://"] ||
[s hasPrefix:@"http://"]) {
// don't unwrap if the following line begins with whitespace,
// or with the word "Copyright", or if it begins with a URL.
static char *
anchorize (const char *url)
{
- const char *wiki = "http://en.wikipedia.org/wiki/";
- const char *math = "http://mathworld.wolfram.com/";
- if (!strncmp (wiki, url, strlen(wiki))) {
+ const char *wiki1 = "http://en.wikipedia.org/wiki/";
+ const char *wiki2 = "https://en.wikipedia.org/wiki/";
+ const char *math1 = "http://mathworld.wolfram.com/";
+ const char *math2 = "https://mathworld.wolfram.com/";
+ if (!strncmp (wiki1, url, strlen(wiki1)) ||
+ !strncmp (wiki2, url, strlen(wiki2))) {
char *anchor = (char *) malloc (strlen(url) * 3 + 10);
strcpy (anchor, "Wikipedia: \"");
- const char *in = url + strlen(wiki);
+ const char *in = url + strlen(!strncmp (wiki1, url, strlen(wiki1))
+ ? wiki1 : wiki2);
char *out = anchor + strlen(anchor);
while (*in) {
if (*in == '_') {
*out = 0;
return anchor;
- } else if (!strncmp (math, url, strlen(math))) {
+ } else if (!strncmp (math1, url, strlen(math1)) ||
+ !strncmp (math2, url, strlen(math2))) {
char *anchor = (char *) malloc (strlen(url) * 3 + 10);
strcpy (anchor, "MathWorld: \"");
- const char *start = url + strlen(wiki);
+ const char *start = url + strlen(!strncmp (math1, url, strlen(math1))
+ ? math1 : math2);
const char *in = start;
char *out = anchor + strlen(anchor);
while (*in) {
NSString *text = [nstext text];
# endif
- int L = [text length];
+ NSUInteger L = [text length];
NSRange start; // range is start-of-search to end-of-string
start.location = 0;
start.length = L;
// Find the beginning of a URL...
//
- NSRange r2 = [text rangeOfString:@"http://" options:0 range:start];
+ NSRange r2 = [text rangeOfString: @"http://" options:0 range:start];
+ NSRange r3 = [text rangeOfString:@"https://" options:0 range:start];
+ if ((r2.location == NSNotFound &&
+ r3.location != NSNotFound) ||
+ (r2.location != NSNotFound &&
+ r3.location != NSNotFound &&
+ r3.location < r2.location))
+ r2 = r3;
if (r2.location == NSNotFound)
break;
// Find the end of a URL (whitespace or EOF)...
//
- NSRange r3 = [text rangeOfCharacterFromSet:
- [NSCharacterSet whitespaceAndNewlineCharacterSet]
- options:0 range:start];
+ r3 = [text rangeOfCharacterFromSet:
+ [NSCharacterSet whitespaceAndNewlineCharacterSet]
+ options:0 range:start];
if (r3.location == NSNotFound) // EOF
r3.location = L, r3.length = 0;
free (anchor);
- int L2 = [text length]; // might have changed
+ NSUInteger L2 = [text length]; // might have changed
start.location -= (L - L2);
L = L2;
}
- (void) parseAttrs:(NSMutableDictionary *)dict node:(NSXMLNode *)node
{
NSArray *attrs = [(NSXMLElement *) node attributes];
- int n = [attrs count];
+ NSUInteger n = [attrs count];
int i;
// For each key in the dictionary, fill in the dict with the corresponding
*/
- (NSString *) parseXScreenSaverTag:(NSXMLNode *)node
{
- NSMutableDictionary *dict =
- [NSMutableDictionary dictionaryWithObjectsAndKeys:
- @"", @"name",
- @"", @"_label",
- @"", @"gl",
- nil];
+ NSMutableDictionary *dict = [@{ @"name": @"",
+ @"_label": @"",
+ @"gl": @"" }
+ mutableCopy];
[self parseAttrs:dict node:node];
NSString *name = [dict objectForKey:@"name"];
NSString *label = [dict objectForKey:@"_label"];
+ [dict release];
+ dict = 0;
NSAssert1 (label, @"no _label in %@", [node name]);
NSAssert1 (name, @"no name in \"%@\"", label);
[lab setAutoresizingMask: (UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleHeight)];
# endif // USE_IPHONE
+ [lab autorelease];
return lab;
}
*/
- (void) makeCheckbox:(NSXMLNode *)node on:(NSView *)parent
{
- NSMutableDictionary *dict =
- [NSMutableDictionary dictionaryWithObjectsAndKeys:
- @"", @"id",
- @"", @"_label",
- @"", @"arg-set",
- @"", @"arg-unset",
- nil];
+ NSMutableDictionary *dict = [@{ @"id": @"",
+ @"_label": @"",
+ @"arg-set": @"",
+ @"arg-unset": @"" }
+ mutableCopy];
[self parseAttrs:dict node:node];
NSString *label = [dict objectForKey:@"_label"];
NSString *arg_set = [dict objectForKey:@"arg-set"];
NSString *arg_unset = [dict objectForKey:@"arg-unset"];
+ [dict release];
+ dict = 0;
if (!label) {
NSAssert1 (0, @"no _label in %@", [node name]);
[self placeChild:lab on:parent];
UISwitch *button = [[UISwitch alloc] initWithFrame:rect];
[self placeChild:button on:parent right:YES];
- [lab release];
# endif // USE_IPHONE
/* Creates the number selection control described by the given XML node.
If "type=slider", it's an NSSlider.
If "type=spinbutton", it's a text field with up/down arrows next to it.
-*/
+ */
- (void) makeNumberSelector:(NSXMLNode *)node on:(NSView *)parent
{
- NSMutableDictionary *dict =
- [NSMutableDictionary dictionaryWithObjectsAndKeys:
- @"", @"id",
- @"", @"_label",
- @"", @"_low-label",
- @"", @"_high-label",
- @"", @"type",
- @"", @"arg",
- @"", @"low",
- @"", @"high",
- @"", @"default",
- @"", @"convert",
- nil];
+ NSMutableDictionary *dict = [@{ @"id": @"",
+ @"_label": @"",
+ @"_low-label": @"",
+ @"_high-label": @"",
+ @"type": @"",
+ @"arg": @"",
+ @"low": @"",
+ @"high": @"",
+ @"default": @"",
+ @"convert": @"" }
+ mutableCopy];
[self parseAttrs:dict node:node];
NSString *label = [dict objectForKey:@"_label"];
NSString *low_label = [dict objectForKey:@"_low-label"];
NSString *high = [dict objectForKey:@"high"];
NSString *def = [dict objectForKey:@"default"];
NSString *cvt = [dict objectForKey:@"convert"];
+ [dict release];
+ dict = 0;
NSAssert1 (arg, @"no arg in %@", label);
NSAssert1 (type, @"no type in %@", label);
while (range2 > max_ticks)
range2 /= 10;
+# ifndef USE_IPHONE
// If we have elided ticks, leave it at the max number of ticks.
if (range != range2 && range2 < max_ticks)
range2 = max_ticks;
if (float_p && range2 < max_ticks)
range2 = max_ticks;
-# ifndef USE_IPHONE
[slider setNumberOfTickMarks:range2];
[slider setAllowsTickMarkValuesOnly:
[lab setFont:[NSFont boldSystemFontOfSize:s]];
}
# endif
- [lab release];
}
if (low_label) {
[self placeChild:lab on:parent];
# else // USE_IPHONE
[lab setTextAlignment: NSTextAlignmentRight];
+ // Sometimes rotation screws up truncation.
+ [lab setLineBreakMode:NSLineBreakByClipping];
[self placeChild:lab on:parent right:(label ? YES : NO)];
# endif // USE_IPHONE
-
- [lab release];
}
# ifndef USE_IPHONE
// Make right label be same height as slider.
rect.size.height = [slider frame].size.height;
[lab setFrame:rect];
+# ifdef USE_IPHONE
+ // Sometimes rotation screws up truncation.
+ [lab setLineBreakMode:NSLineBreakByClipping];
+# endif
[self placeChild:lab on:parent right:YES];
- [lab release];
}
[self bindSwitch:slider cmdline:arg];
rect.size.height = [txt frame].size.height;
[lab setFrame:rect];
[self placeChild:lab on:parent];
- [lab release];
}
[self placeChild:txt on:parent right:(label ? YES : NO)];
/* Creates the popup menu described by the given XML node (and its children).
-*/
+ */
- (void) makeOptionMenu:(NSXMLNode *)node on:(NSView *)parent
{
NSArray *children = [node children];
- int i, count = [children count];
+ NSUInteger i, count = [children count];
if (count <= 0) {
NSAssert1 (0, @"no menu items in \"%@\"", [node name]);
// get the "id" attribute off the <select> tag.
//
- NSMutableDictionary *dict =
- [NSMutableDictionary dictionaryWithObjectsAndKeys:
- @"", @"id",
- nil];
+ NSMutableDictionary *dict = [@{ @"id": @"", } mutableCopy];
[self parseAttrs:dict node:node];
+ [dict release];
+ dict = 0;
NSRect rect;
rect.origin.x = rect.origin.y = 0;
// get the "id", "_label", and "arg-set" attrs off of the <option> tags.
//
- NSMutableDictionary *dict2 =
- [NSMutableDictionary dictionaryWithObjectsAndKeys:
- @"", @"id",
- @"", @"_label",
- @"", @"arg-set",
- nil];
+ NSMutableDictionary *dict2 = [@{ @"id": @"",
+ @"_label": @"",
+ @"arg-set": @"" }
+ mutableCopy];
[self parseAttrs:dict2 node:child];
NSString *label = [dict2 objectForKey:@"_label"];
NSString *arg_set = [dict2 objectForKey:@"arg-set"];
+ [dict2 release];
+ dict2 = 0;
if (!label) {
NSAssert1 (0, @"no _label in %@", [child name]);
// Store the items for this picker in the picker_values array.
// This is so fucking stupid.
- int menu_number = [pref_keys count] - 1;
+ unsigned long menu_number = [pref_keys count] - 1;
if (! picker_values)
picker_values = [[NSMutableArray arrayWithCapacity:menu_number] retain];
while ([picker_values count] <= menu_number)
[self placeSeparator];
i = 0;
- for (NSArray *item in items) {
- RadioButton *b = [[RadioButton alloc] initWithIndex:i
+ for (__attribute__((unused)) NSArray *item in items) {
+ RadioButton *b = [[RadioButton alloc] initWithIndex: (int)i
items:items];
[b setLineBreakMode:NSLineBreakByTruncatingHead];
[b setFont:[NSFont boldSystemFontOfSize: FONT_SIZE]];
[self placeChild:b on:parent];
+ [b release];
i++;
}
{
NSString *text = nil;
NSArray *children = [node children];
- int i, count = [children count];
+ NSUInteger i, count = [children count];
for (i = 0; i < count; i++) {
NSXMLNode *child = [children objectAtIndex:i];
rect.size.height = 50; // sized later
# ifndef USE_IPHONE
NSText *lab = [[NSText alloc] initWithFrame:rect];
+ [lab autorelease];
[lab setEditable:NO];
[lab setDrawsBackground:NO];
[lab setHorizontallyResizable:YES];
HTMLLabel *lab = [[HTMLLabel alloc]
initWithText:text
font:[NSFont systemFontOfSize: [NSFont systemFontSize]]];
+ [lab autorelease];
[lab setFrame:rect];
[lab sizeToFit];
# endif // USE_HTML_LABELS
# endif // USE_IPHONE
[self placeChild:lab on:parent];
- [lab release];
}
/* Creates the NSTextField described by the given XML node.
-*/
+ */
- (void) makeTextField: (NSXMLNode *)node
on: (NSView *)parent
withLabel: (BOOL) label_p
horizontal: (BOOL) horiz_p
{
- NSMutableDictionary *dict =
- [NSMutableDictionary dictionaryWithObjectsAndKeys:
- @"", @"id",
- @"", @"_label",
- @"", @"arg",
- nil];
+ NSMutableDictionary *dict = [@{ @"id": @"",
+ @"_label": @"",
+ @"arg": @"" }
+ mutableCopy];
[self parseAttrs:dict node:node];
NSString *label = [dict objectForKey:@"_label"];
NSString *arg = [dict objectForKey:@"arg"];
+ [dict release];
+ dict = 0;
if (!label && label_p) {
NSAssert1 (0, @"no _label in %@", [node name]);
if (label) {
LABEL *lab = [self makeLabel:label];
[self placeChild:lab on:parent];
- [lab release];
}
[self placeChild:txt on:parent right:(label ? YES : NO)];
/* Creates the NSTextField described by the given XML node,
and hooks it up to a Choose button and a file selector widget.
-*/
+ */
- (void) makeFileSelector: (NSXMLNode *)node
on: (NSView *)parent
dirsOnly: (BOOL) dirsOnly
editable: (BOOL) editable_p
{
# ifndef USE_IPHONE // No files. No selectors.
- NSMutableDictionary *dict =
- [NSMutableDictionary dictionaryWithObjectsAndKeys:
- @"", @"id",
- @"", @"_label",
- @"", @"arg",
- nil];
+ NSMutableDictionary *dict = [@{ @"id": @"",
+ @"_label": @"",
+ @"arg": @"" }
+ mutableCopy];
[self parseAttrs:dict node:node];
NSString *label = [dict objectForKey:@"_label"];
NSString *arg = [dict objectForKey:@"arg"];
+ [dict release];
+ dict = 0;
if (!label && label_p) {
NSAssert1 (0, @"no _label in %@", [node name]);
if (label) {
lab = [self makeLabel:label];
[self placeChild:lab on:parent];
- [lab release];
}
[self placeChild:txt on:parent right:(label ? YES : NO)];
[panel setCanChooseFiles:!dirs_p];
[panel setCanChooseDirectories:dirs_p];
- NSString *file = [txt stringValue];
- if ([file length] <= 0) {
- file = NSHomeDirectory();
- if (dirs_p)
- file = [file stringByAppendingPathComponent:@"Pictures"];
- }
-
-// NSString *dir = [file stringByDeletingLastPathComponent];
-
- int result = [panel runModalForDirectory:file //dir
- file:nil //[file lastPathComponent]
- types:nil];
+ NSInteger result = [panel runModal];
if (result == NSOKButton) {
- NSArray *files = [panel filenames];
- file = ([files count] > 0 ? [files objectAtIndex:0] : @"");
+ NSArray *files = [panel URLs];
+ NSString *file = ([files count] > 0 ? [[files objectAtIndex:0] path] : @"");
file = [file stringByAbbreviatingWithTildeInPath];
[txt setStringValue:file];
if ([path hasPrefix:@"values."]) // WTF.
path = [path substringFromIndex:7];
[[prefs values] setValue:file forKey:path];
-
-#if 0
- // make sure the end of the string is visible.
- NSText *fe = [[txt window] fieldEditor:YES forObject:txt];
- NSRange range;
- range.location = [file length]-3;
- range.length = 1;
- if (! [[txt window] makeFirstResponder:[txt window]])
- [[txt window] endEditingFor:nil];
-// [[txt window] makeFirstResponder:nil];
- [fe setSelectedRange:range];
-// [tv scrollRangeToVisible:range];
-// [txt setNeedsDisplay:YES];
-// [[txt cell] setNeedsDisplay:YES];
-// [txt selectAll:txt];
-#endif
}
}
{
NSView *parent = [button superview];
NSArray *kids = [parent subviews];
- int nkids = [kids count];
+ NSUInteger nkids = [kids count];
int i;
NSTextField *f = 0;
for (i = 0; i < nkids; i++) {
[self placeChild:matrix on:group];
[self placeChild:rgroup on:group right:YES];
+ [proto release];
+ [matrix release];
+ [rgroup release];
NSXMLNode *node2;
// </select>
node2 = [[NSXMLElement alloc] initWithName:@"select"];
- [node2 setAttributesAsDictionary:
- [NSDictionary dictionaryWithObjectsAndKeys:
- @"textMode", @"id",
- nil]];
+ [node2 setAttributesAsDictionary:@{ @"id": @"textMode" }];
NSXMLNode *node3 = [[NSXMLElement alloc] initWithName:@"option"];
[node3 setAttributesAsDictionary:
- [NSDictionary dictionaryWithObjectsAndKeys:
- @"date", @"id",
- @"-text-mode date", @"arg-set",
- @"Display the date and time", @"_label",
- nil]];
+ @{ @"id": @"date",
+ @"arg-set": @"-text-mode date",
+ @"_label": @"Display the date and time" }];
[node3 setParent: node2];
- //[node3 release];
+ [node3 autorelease];
node3 = [[NSXMLElement alloc] initWithName:@"option"];
[node3 setAttributesAsDictionary:
- [NSDictionary dictionaryWithObjectsAndKeys:
- @"text", @"id",
- @"-text-mode literal", @"arg-set",
- @"Display static text", @"_label",
- nil]];
+ @{ @"id": @"text",
+ @"arg-set": @"-text-mode literal",
+ @"_label": @"Display static text" }];
[node3 setParent: node2];
- //[node3 release];
+ [node3 autorelease];
node3 = [[NSXMLElement alloc] initWithName:@"option"];
[node3 setAttributesAsDictionary:
- [NSDictionary dictionaryWithObjectsAndKeys:
- @"url", @"id",
- @"Display the contents of a URL", @"_label",
- nil]];
+ @{ @"id": @"url",
+ @"_label": @"Display the contents of a URL" }];
[node3 setParent: node2];
- //[node3 release];
+ [node3 autorelease];
[self makeOptionMenu:node2 on:rgroup];
+ [node2 release];
# endif // USE_IPHONE
// <string id="textLiteral" _label="" arg-set="-text-literal %"/>
node2 = [[NSXMLElement alloc] initWithName:@"string"];
[node2 setAttributesAsDictionary:
- [NSDictionary dictionaryWithObjectsAndKeys:
- @"textLiteral", @"id",
- @"-text-literal %", @"arg",
+ @{ @"id": @"textLiteral",
+ @"arg": @"-text-literal %",
# ifdef USE_IPHONE
- @"Text to display", @"_label",
+ @"_label": @"Text to display"
# endif
- nil]];
+ }];
[self makeTextField:node2 on:rgroup
# ifndef USE_IPHONE
withLabel:NO
withLabel:YES
# endif
horizontal:NO];
+ [node2 release];
// rect = [last_child(rgroup) frame];
toObject:[matrix cellAtRow:1 column:0]
withKeyPath:@"value"
options:nil];
-*/
+ */
# ifndef USE_IPHONE
// <file id="textFile" _label="" arg-set="-text-file %"/>
node2 = [[NSXMLElement alloc] initWithName:@"string"];
[node2 setAttributesAsDictionary:
- [NSDictionary dictionaryWithObjectsAndKeys:
- @"textFile", @"id",
- @"-text-file %", @"arg",
- nil]];
+ @{ @"id": @"textFile",
+ @"arg": @"-text-file %" }];
[self makeFileSelector:node2 on:rgroup
dirsOnly:NO withLabel:NO editable:NO];
+ [node2 release];
# endif // !USE_IPHONE
// rect = [last_child(rgroup) frame];
// <string id="textURL" _label="" arg-set="text-url %"/>
node2 = [[NSXMLElement alloc] initWithName:@"string"];
[node2 setAttributesAsDictionary:
- [NSDictionary dictionaryWithObjectsAndKeys:
- @"textURL", @"id",
- @"-text-url %", @"arg",
+ @{ @"id": @"textURL",
+ @"arg": @"-text-url %",
# ifdef USE_IPHONE
- @"URL to display", @"_label",
+ @"_label": @"URL to display",
# endif
- nil]];
+ }];
[self makeTextField:node2 on:rgroup
# ifndef USE_IPHONE
withLabel:NO
withLabel:YES
# endif
horizontal:NO];
+ [node2 release];
// rect = [last_child(rgroup) frame];
// <string id="textProgram" _label="" arg-set="text-program %"/>
node2 = [[NSXMLElement alloc] initWithName:@"string"];
[node2 setAttributesAsDictionary:
- [NSDictionary dictionaryWithObjectsAndKeys:
- @"textProgram", @"id",
- @"-text-program %", @"arg",
- nil]];
+ @{ @"id": @"textProgram",
+ @"arg": @"-text-program %",
+ }];
[self makeTextField:node2 on:rgroup withLabel:NO horizontal:NO];
+ [node2 release];
}
// rect = [last_child(rgroup) frame];
[box sizeToFit];
[self placeChild:box on:parent];
+ [group release];
+ [box release];
# endif // !USE_IPHONE
}
node2 = [[NSXMLElement alloc] initWithName:@"boolean"];
[node2 setAttributesAsDictionary:
- [NSDictionary dictionaryWithObjectsAndKeys:
- @"grabDesktopImages", @"id",
- @ SCREENS, @"_label",
- @"-no-grab-desktop", @"arg-unset",
- nil]];
+ @{ @"id": @"grabDesktopImages",
+ @"_label": @ SCREENS,
+ @"arg-unset": @"-no-grab-desktop",
+ }];
[self makeCheckbox:node2 on:parent];
+ [node2 release];
node2 = [[NSXMLElement alloc] initWithName:@"boolean"];
[node2 setAttributesAsDictionary:
- [NSDictionary dictionaryWithObjectsAndKeys:
- @"chooseRandomImages", @"id",
- @ PHOTOS, @"_label",
- @"-choose-random-images", @"arg-set",
- nil]];
+ @{ @"id": @"chooseRandomImages",
+ @"_label": @ PHOTOS,
+ @"arg-set": @"-choose-random-images",
+ }];
[self makeCheckbox:node2 on:parent];
+ [node2 release];
node2 = [[NSXMLElement alloc] initWithName:@"string"];
[node2 setAttributesAsDictionary:
- [NSDictionary dictionaryWithObjectsAndKeys:
- @"imageDirectory", @"id",
- @"Images from:", @"_label",
- @"-image-directory %", @"arg",
- nil]];
+ @{ @"id": @"imageDirectory",
+ @"_label": @"Images from:",
+ @"arg": @"-image-directory %",
+ }];
[self makeFileSelector:node2 on:parent
dirsOnly:YES withLabel:YES editable:YES];
+ [node2 release];
# undef SCREENS
# undef PHOTOS
r2.origin.x += 20;
r2.origin.y += 14;
[lab2 setFrameOrigin:r2.origin];
- [lab2 release];
# endif // USE_IPHONE
}
+- (void) makeUpdaterControlBox:(NSXMLNode *)node on:(NSView *)parent
+{
+# ifndef USE_IPHONE
+ /*
+ [x] Check for Updates [ Monthly ]
+
+ <hgroup>
+ <boolean id="automaticallyChecksForUpdates"
+ _label="Automatically check for updates"
+ arg-unset="-no-automaticallyChecksForUpdates" />
+ <select id="updateCheckInterval">
+ <option="hourly" _label="Hourly" arg-set="-updateCheckInterval 3600"/>
+ <option="daily" _label="Daily" arg-set="-updateCheckInterval 86400"/>
+ <option="weekly" _label="Weekly" arg-set="-updateCheckInterval 604800"/>
+ <option="monthly" _label="Monthly" arg-set="-updateCheckInterval 2629800"/>
+ </select>
+ </hgroup>
+ */
+
+ // <hgroup>
+
+ NSRect rect;
+ rect.size.width = rect.size.height = 1;
+ rect.origin.x = rect.origin.y = 0;
+ NSView *group = [[NSView alloc] initWithFrame:rect];
+
+ NSXMLElement *node2;
+
+ // <boolean ...>
+
+ node2 = [[NSXMLElement alloc] initWithName:@"boolean"];
+ [node2 setAttributesAsDictionary:
+ @{ @"id": @SUSUEnableAutomaticChecksKey,
+ @"_label": @"Automatically check for updates",
+ @"arg-unset": @"-no-" SUSUEnableAutomaticChecksKey,
+ }];
+ [self makeCheckbox:node2 on:group];
+ [node2 release];
+
+ // <select ...>
+
+ node2 = [[NSXMLElement alloc] initWithName:@"select"];
+ [node2 setAttributesAsDictionary:
+ @{ @"id": @SUScheduledCheckIntervalKey }];
+
+ // <option ...>
+
+ NSXMLNode *node3 = [[NSXMLElement alloc] initWithName:@"option"];
+ [node3 setAttributesAsDictionary:
+ @{ @"id": @"hourly",
+ @"arg-set": @"-" SUScheduledCheckIntervalKey " 3600",
+ @"_label": @"Hourly" }];
+ [node3 setParent: node2];
+ [node3 autorelease];
+
+ node3 = [[NSXMLElement alloc] initWithName:@"option"];
+ [node3 setAttributesAsDictionary:
+ @{ @"id": @"daily",
+ @"arg-set": @"-" SUScheduledCheckIntervalKey " 86400",
+ @"_label": @"Daily" }];
+ [node3 setParent: node2];
+ [node3 autorelease];
+
+ node3 = [[NSXMLElement alloc] initWithName:@"option"];
+ [node3 setAttributesAsDictionary:
+ @{ @"id": @"weekly",
+ // @"arg-set": @"-" SUScheduledCheckIntervalKey " 604800",
+ @"_label": @"Weekly",
+ }];
+ [node3 setParent: node2];
+ [node3 autorelease];
+
+ node3 = [[NSXMLElement alloc] initWithName:@"option"];
+ [node3 setAttributesAsDictionary:
+ @{ @"id": @"monthly",
+ @"arg-set": @"-" SUScheduledCheckIntervalKey " 2629800",
+ @"_label": @"Monthly",
+ }];
+ [node3 setParent: node2];
+ [node3 autorelease];
+
+ // </option>
+ [self makeOptionMenu:node2 on:group];
+ [node2 release];
+
+ // </hgroup>
+ layout_group (group, TRUE);
+
+ rect.size.width = rect.size.height = 0;
+ NSBox *box = [[NSBox alloc] initWithFrame:rect];
+ [box setTitlePosition:NSNoTitle];
+ [box setBorderType:NSNoBorder];
+ [box setContentViewMargins:rect.size];
+ [box setContentView:group];
+ [box sizeToFit];
+
+ [self placeChild:box on:parent];
+
+ [group release];
+ [box release];
+
+# endif // !USE_IPHONE
+}
+
+
#pragma mark Layout for controls
last_child (NSView *parent)
{
NSArray *kids = [parent subviews];
- int nkids = [kids count];
+ NSUInteger nkids = [kids count];
if (nkids == 0)
return 0;
else
# else // USE_IPHONE
- // Controls is an array of arrays of the controls, divided into sections.
+ /* Controls is an array of arrays of the controls, divided into sections.
+ Each hgroup / vgroup gets a nested array, too, e.g.:
+
+ [ [ [ <label>, <checkbox> ],
+ [ <label>, <checkbox> ],
+ [ <label>, <checkbox> ] ],
+ [ <label>, <text-field> ],
+ [ <label>, <low-label>, <slider>, <high-label> ],
+ [ <low-label>, <slider>, <high-label> ],
+ <HTML-label>
+ ];
+
+ If an element begins with a label, it is terminal, otherwise it is a
+ group. There are (currently) never more than 4 elements in a single
+ terminal element.
+
+ A blank vertical spacer is placed between each hgroup / vgroup,
+ by making each of those a new section in the TableView.
+ */
if (! controls)
controls = [[NSMutableArray arrayWithCapacity:10] retain];
if ([controls count] == 0)
NSAssert ([(NSArray *) old count] < 4, @"internal error");
[(NSMutableArray *) old addObject: child];
} else {
- // Replace the control in this cell with an array, then app
+ // Replace the control in this cell with an array, then append
NSMutableArray *a = [NSMutableArray arrayWithObjects: old, child, nil];
[current replaceObjectAtIndex:[current count]-1 withObject:a];
}
[box sizeToFit];
[self placeChild:box on:parent];
+ [group release];
+ [box release];
# endif // !USE_IPHONE
}
layout_group (NSView *group, BOOL horiz_p)
{
NSArray *kids = [group subviews];
- int nkids = [kids count];
- int i;
+ NSUInteger nkids = [kids count];
+ NSUInteger i;
double maxx = 0, miny = 0;
for (i = 0; i < nkids; i++) {
NSView *kid = [kids objectAtIndex:i];
} else if ([name isEqualToString:@"command"]) {
// do nothing: this is the "-root" business
+ } else if ([name isEqualToString:@"video"]) {
+ // ignored
+
} else if ([name isEqualToString:@"boolean"]) {
[self makeCheckbox:node on:parent];
} else if ([name isEqualToString:@"xscreensaver-image"]) {
[self makeImageLoaderControlBox:node on:parent];
+ } else if ([name isEqualToString:@"xscreensaver-updater"]) {
+ [self makeUpdaterControlBox:node on:parent];
+
} else {
NSAssert1 (0, @"unknown tag: %@", name);
}
- (void)traverseChildren:(NSXMLNode *)node on:(NSView *)parent
{
NSArray *children = [node children];
- int i, count = [children count];
+ NSUInteger i, count = [children count];
for (i = 0; i < count; i++) {
NSXMLNode *child = [children objectAtIndex:i];
[self makeControl:child on:parent];
{
NSRect f;
NSArray *kids = [parent subviews];
- int nkids = [kids count];
+ NSUInteger nkids = [kids count];
NSView *text = 0; // the NSText at the bottom of the window
double maxx = 0, miny = 0;
- int i;
+ NSUInteger i;
/* Find the size of the rectangle taken up by each of the children
except the final "NSText" child.
}
/*
-Bad:
- parent: 420 x 541 @ 0 0
- text: 380 x 100 @ 20 22 miny=-501
+ Bad:
+ parent: 420 x 541 @ 0 0
+ text: 380 x 100 @ 20 22 miny=-501
-Good:
- parent: 420 x 541 @ 0 0
- text: 380 x 100 @ 20 50 miny=-501
-*/
+ Good:
+ parent: 420 x 541 @ 0 0
+ text: 380 x 100 @ 20 50 miny=-501
+ */
// #### WTF2: See "WTF" above. If the text field is off the screen,
// move it up. We need this on 10.6 but not on 10.5. Auugh.
[cancel setAction:@selector(cancelAction:)];
[reset setAction:@selector(resetAction:)];
+ [bbox release];
+
return pbox;
}
#endif // !USE_IPHONE
fix_contentview_size (panel);
NSView *root = wrap_with_buttons (parent, panel);
- [userDefaultsController setAppliesImmediately:NO];
+ [userDefaultsController setAppliesImmediately:NO];
+ [globalDefaultsController setAppliesImmediately:NO];
[panel setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
[parent setMinSize:rect.size];
[parent setContentView:root];
+
+ [panel release];
+ [root release];
# else // USE_IPHONE
attributes:(NSDictionary *)attrs
{
NSXMLElement *e = [[NSXMLElement alloc] initWithName:elt];
+ [e autorelease];
[e setKind:SimpleXMLElementKind];
[e setAttributesAsDictionary:attrs];
NSXMLElement *p = xml_parsing;
NSXMLElement *p = xml_parsing;
[e setParent:p];
[e setObjectValue: string];
+ [e autorelease];
}
CGFloat max = 0;
for (NSArray *a2 in a) {
NSString *s = [a2 objectAtIndex:0];
+ // #### sizeWithFont deprecated as of iOS 7; use boundingRectWithSize.
CGSize r = [s sizeWithFont:f];
if (r.width > max) max = r.width;
}
[[self navigationItem]
setRightBarButtonItem: [[UIBarButtonItem alloc]
initWithTitle: @"Reset to Defaults"
- style: UIBarButtonItemStyleBordered
+ style: UIBarButtonItemStylePlain
target:self
action:@selector(resetAction:)]];
NSString *s = saver_name;
- (CGFloat)tableView:(UITableView *)tv
heightForRowAtIndexPath:(NSIndexPath *)ip
{
- CGFloat h = [tv rowHeight];
+ CGFloat h = 0;
NSView *ctl = [[controls objectAtIndex:[ip indexAtPosition:0]]
objectAtIndex:[ip indexAtPosition:1]];
if ([ctl isKindOfClass:[NSArray class]]) {
NSArray *set = (NSArray *) ctl;
switch ([set count]) {
- case 4:
-# ifdef LABEL_ABOVE_SLIDER
- h *= 1.7; break; // label + left/slider/right: 2 1/2 lines
-# endif
- case 3: h *= 1.2; break; // left/slider/right: 1 1/2 lines
- case 2:
- if ([[set objectAtIndex:1] isKindOfClass:[UITextField class]])
- h *= 1.2;
+ case 4: // label + left/slider/right.
+ case 3: // left/slider/right.
+ h = FONT_SIZE * 3.0;
+ break;
+ case 2: // Checkboxes, or text fields.
+ h = FONT_SIZE * 2.4;
break;
}
} 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
+ // Radio buttons in a multi-select list.
+ h = FONT_SIZE * 1.9;
+# ifdef USE_HTML_LABELS
} else if ([ctl isKindOfClass:[HTMLLabel class]]) {
HTMLLabel *t = (HTMLLabel *) ctl;
[t setFrame:r];
[t sizeToFit];
r = t.frame;
- h = r.size.height + LINE_SPACING * 3;
-
+ h = r.size.height;
# endif // USE_HTML_LABELS
- } else {
- CGFloat h2 = [ctl frame].size.height;
- h2 += LINE_SPACING * 2;
- if (h2 > h) h = h2;
+
+ } else { // Does this ever happen?
+ h = FONT_SIZE + LINE_SPACING * 2;
}
+ if (h <= 0) abort();
return h;
}
{
UITableView *tv = (UITableView *) [self view];
NSMutableArray *a = [NSMutableArray arrayWithCapacity:20];
- int rows = [self numberOfSectionsInTableView:tv];
+ NSInteger rows = [self numberOfSectionsInTableView:tv];
for (int i = 0; i < rows; i++) {
- int cols = [self tableView:tv numberOfRowsInSection:i];
+ NSInteger cols = [self tableView:tv numberOfRowsInSection:i];
for (int j = 0; j < cols; j++) {
NSUInteger ip[2];
ip[0] = i;
[tv beginUpdates];
[tv reloadRowsAtIndexPaths:a withRowAnimation:UITableViewRowAnimationNone];
[tv endUpdates];
+
+ // Default opacity looks bad.
+ // #### Oh great, this only works *sometimes*.
+ UIView *v = [[self navigationItem] titleView];
+ [v setBackgroundColor:[[v backgroundColor] colorWithAlphaComponent:1]];
}
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];
+
+ NSObject *current = [[self controllerForKey:pref_key] objectForKey:pref_key];
// Convert them both to strings and compare those, so that
// we don't get screwed by int 1 versus string "1".
- (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;
+ CGFloat ww = [tv frame].size.width;
+ CGFloat hh = [self tableView:tv heightForRowAtIndexPath:ip];
- CGRect p = [cell frame];
- CGRect r;
+ float os_version = [[[UIDevice currentDevice] systemVersion] floatValue];
- p.size.height = [self tableView:tv heightForRowAtIndexPath:ip];
- [cell setFrame:p];
+ // Width of the column of labels on the left.
+ CGFloat left_width = ww * 0.4;
+ CGFloat right_edge = ww - LEFT_MARGIN;
- // 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;
+ if (os_version < 7) // margins were wider on iOS 6.1
+ right_edge -= 10;
+ CGFloat max = FONT_SIZE * 12;
+ if (left_width > max) left_width = max;
NSView *ctl = [[controls objectAtIndex:[ip indexAtPosition:0]]
- objectAtIndex:[ip indexAtPosition:1]];
+ objectAtIndex:[ip indexAtPosition:1]];
if ([ctl isKindOfClass:[NSArray class]]) {
// This cell has a set of objects in it.
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;
+ CGRect r = [ctl frame];
+
+ if ([ctl isKindOfClass:[UISwitch class]]) { // Checkboxes.
r.size.width = 80; // Magic.
- r.origin.x = right_edge - r.size.width;
+ r.origin.x = right_edge - r.size.width + 30; // beats me
+
+ if (os_version < 7) // checkboxes were wider on iOS 6.1
+ r.origin.x -= 25;
+
} else {
- // Expandable sliders.
- ctl.autoresizingMask = UIViewAutoresizingFlexibleWidth;
- r.origin.x = left_edge;
+ r.origin.x = left_width; // Text fields, etc.
r.size.width = right_edge - r.origin.x;
}
- r.origin.y = (p.size.height - r.size.height) / 2;
+
+ r.origin.y = (hh - r.size.height) / 2; // Center vertically.
[ctl setFrame:r];
- // Make a box.
- NSView *box = [[UIView alloc] initWithFrame:p];
+ // Make a box and put the label and checkbox/slider into it.
+ r.origin.x = 0;
+ r.origin.y = 0;
+ r.size.width = ww;
+ r.size.height = hh;
+ NSView *box = [[UIView alloc] initWithFrame:r];
[box addSubview: ctl];
- // cell.textLabel.text = [(UILabel *) ctl text];
+ // Let the label make use of any space not taken up by the control.
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;
+ r.size.height = hh;
[label setFrame:r];
[label setFont:[NSFont boldSystemFontOfSize: FONT_SIZE]];
- label.autoresizingMask = UIViewAutoresizingFlexibleRightMargin;
- box. autoresizingMask = UIViewAutoresizingFlexibleWidth;
[box addSubview: label];
+ [box autorelease];
ctl = box;
}
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.
+ // With 3 elements, 1 and 3 are labels.
+ // With 4 elements, 1, 2 and 4 are labels.
int i = 0;
UILabel *top = ([set count] == 4
? [set objectAtIndex: i++]
// 3 elements: control at top of cell.
// 4 elements: center the control vertically.
- r = [mid frame];
+ CGRect r = [mid frame];
+ r.size.height = 32; // Unchangable height of the slider thumb.
+
+ // Center the slider between left_width and right_edge.
# ifdef LABEL_ABOVE_SLIDER
- left_edge = LEFT_MARGIN;
+ r.origin.x = LEFT_MARGIN;
+# else
+ r.origin.x = left_width;
# endif
- r.origin.x = left_edge;
+ r.origin.y = (hh - r.size.height) / 2;
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) {
+# ifdef LABEL_ABOVE_SLIDER
+ // Top label goes above, flush center/top.
+ r.origin.x = (ww - r.size.width) / 2;
+ r.origin.y = 4;
+ // #### sizeWithFont deprecated as of iOS 7; use boundingRectWithSize.
r.size = [[top text] sizeWithFont:[top font]
constrainedToSize:
- CGSizeMake (p.size.width - LEFT_MARGIN*2,
- 100000)
+ CGSizeMake (ww - LEFT_MARGIN*2, 100000)
lineBreakMode:[top lineBreakMode]];
- r.origin.x = (p.size.width - r.size.width) / 2;
- r.origin.y = 4;
+# else // !LABEL_ABOVE_SLIDER
+ // Label goes on the left.
+ r.origin.x = LEFT_MARGIN;
+ r.origin.y = 0;
+ r.size.width = left_width - LEFT_MARGIN;
+ r.size.height = hh;
+# endif // !LABEL_ABOVE_SLIDER
[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];
+ left.frame = CGRectMake([mid frame].origin.x, hh - 4,
+ ww - LEFT_MARGIN*2, 100000);
+ [left sizeToFit];
+ r = left.frame;
+ r.origin.y -= r.size.height;
+ left.frame = 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;
+ right.frame =
+ CGRectMake([mid frame].origin.x + [mid frame].size.width,
+ [left frame].origin.y, ww - LEFT_MARGIN*2, 1000000);
+ [right sizeToFit];
+ r = right.frame;
+ r.origin.x -= r.size.width;
+ right.frame = r;
+
+ // Make a box and put the labels and slider into it.
+ r.origin.x = 0;
+ r.origin.y = 0;
+ r.size.width = ww;
+ r.size.height = hh;
+ NSView *box = [[UIView alloc] initWithFrame:r];
+ if (top)
+ [box addSubview: top];
+ [box addSubview: left];
+ [box addSubview: right];
+ [box addSubview: mid];
+ [box autorelease];
+
+ ctl = box;
}
break;
default:
NSAssert (0, @"unhandled size");
}
- } else {
- // A single view, not a pair.
-
- r = [ctl frame];
+ } else { // A single view, not a pair.
+ CGRect r = [ctl frame];
r.origin.x = LEFT_MARGIN;
- r.origin.y = LINE_SPACING;
+ r.origin.y = 0;
r.size.width = right_edge - r.origin.x;
+ r.size.height = hh;
[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];
- }
+ NSString *id = @"Cell";
+ UITableViewCell *cell = [tv dequeueReusableCellWithIdentifier:id];
+ if (!cell)
+ cell = [[[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault
+ reuseIdentifier: id]
+ autorelease];
+ for (UIView *subview in [cell.contentView subviews])
+ [subview removeFromSuperview];
[cell.contentView addSubview: ctl];
+ CGRect r = [ctl frame];
+ r.origin.x = 0;
+ r.origin.y = 0;
+ [cell setFrame:r];
+ cell.selectionStyle = UITableViewCellSelectionStyleNone;
+ [cell setAccessoryType:UITableViewCellAccessoryNone];
+
+# ifndef USE_PICKER_VIEW
+ if ([ctl isKindOfClass:[RadioButton class]])
+ [self updateRadioGroupCell:cell button:(RadioButton *)ctl];
+# endif // USE_PICKER_VIEW
return cell;
}
- (id)initWithXML: (NSData *) xml_data
options: (const XrmOptionDescRec *) _opts
controller: (NSUserDefaultsController *) _prefs
+ globalController: (NSUserDefaultsController *) _globalPrefs
defaults: (NSDictionary *) _defs
{
# ifndef USE_IPHONE
// instance variables
opts = _opts;
defaultOptions = _defs;
- userDefaultsController = _prefs;
- [userDefaultsController retain];
+ userDefaultsController = [_prefs retain];
+ globalDefaultsController = [_globalPrefs retain];
NSXMLParser *xmlDoc = [[NSXMLParser alloc] initWithData:xml_data];
if (!xmlDoc) {
- NSAssert1 (0, @"XML Error: %@", xml_data);
+ NSAssert1 (0, @"XML Error: %@",
+ [[NSString alloc] initWithData:xml_data
+ encoding:NSUTF8StringEncoding]);
return nil;
}
[xmlDoc setDelegate:self];
if (! [xmlDoc parse]) {
NSError *err = [xmlDoc parserError];
- NSAssert2 (0, @"XML Error: %@: %@", xml_data, err);
+ NSAssert2 (0, @"XML Error: %@: %@",
+ [[NSString alloc] initWithData:xml_data
+ encoding:NSUTF8StringEncoding],
+ err);
return nil;
}
+# ifndef USE_IPHONE
+ TextModeTransformer *t = [[TextModeTransformer alloc] init];
+ [NSValueTransformer setValueTransformer:t
+ forName:@"TextModeTransformer"];
+ [t release];
+# endif // USE_IPHONE
+
[self traverseTree];
xml_root = 0;
{
[saver_name release];
[userDefaultsController release];
+ [globalDefaultsController release];
# ifdef USE_IPHONE
[controls release];
[pref_keys release];