X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=OSX%2FXScreenSaverConfigSheet.m;h=2b0cb916bf35a9e2269ab3d6a35d7f439de11863;hb=ec8d2b32b63649e6d32bdfb306eda062769af823;hp=9601eac42c1706c12aa63e1a9652d192c58b3ab6;hpb=6b1c86cf395f59389e4ece4ea8f4bea2c332745b;p=xscreensaver diff --git a/OSX/XScreenSaverConfigSheet.m b/OSX/XScreenSaverConfigSheet.m index 9601eac4..2b0cb916 100644 --- a/OSX/XScreenSaverConfigSheet.m +++ b/OSX/XScreenSaverConfigSheet.m @@ -1,4 +1,4 @@ -/* xscreensaver, Copyright (c) 2006 Jamie Zawinski +/* xscreensaver, Copyright (c) 2006-2011 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -172,7 +172,7 @@ parse_attrs (NSMutableDictionary *dict, NSXMLNode *node) if (! old) { NSAssert2 (0, @"unknown attribute \"%@\" in \"%@\"", key, [node name]); } else if ([old length] != 0) { - NSAssert2 (0, @"duplicate %@: \"%@\", \"%@\"", old, val); + NSAssert3 (0, @"duplicate %@: \"%@\", \"%@\"", key, old, val); } else { [dict setValue:val forKey:key]; } @@ -374,7 +374,8 @@ make_file_selector (NSUserDefaultsController *prefs, const XrmOptionDescRec *opts, NSView *parent, NSXMLNode *node, BOOL dirs_only_p, - BOOL no_label_p) + BOOL no_label_p, + BOOL editable_text_p) { NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys: @@ -404,9 +405,9 @@ make_file_selector (NSUserDefaultsController *prefs, [txt setStringValue:@"123456789 123456789 "]; [txt sizeToFit]; [txt setSelectable:YES]; - [txt setEditable:NO]; - [txt setBezeled:NO]; - [txt setDrawsBackground:NO]; + [txt setEditable:editable_text_p]; + [txt setBezeled:editable_text_p]; + [txt setDrawsBackground:editable_text_p]; [[txt cell] setWraps:NO]; [[txt cell] setScrollable:YES]; [[txt cell] setLineBreakMode:NSLineBreakByTruncatingHead]; @@ -424,10 +425,12 @@ make_file_selector (NSUserDefaultsController *prefs, bind_switch_to_preferences (prefs, txt, arg, opts); [txt release]; - // Make the text field be the same height as the label. + // Make the text field and label be the same height, whichever is taller. if (lab) { rect = [txt frame]; - rect.size.height = [lab frame].size.height; + rect.size.height = ([lab frame].size.height > [txt frame].size.height + ? [lab frame].size.height + : [txt frame].size.height); [txt setFrame:rect]; } @@ -483,7 +486,7 @@ do_file_selector (NSTextField *txt, BOOL dirs_p) types:nil]; if (result == NSOKButton) { NSArray *files = [panel filenames]; - NSString *file = ([files count] > 0 ? [files objectAtIndex:0] : @""); + file = ([files count] > 0 ? [files objectAtIndex:0] : @""); file = [file stringByAbbreviatingWithTildeInPath]; [txt setStringValue:file]; @@ -606,12 +609,21 @@ make_number_selector (NSUserDefaultsController *prefs, label); } + // If either the min or max field contains a decimal point, then this + // option may have a floating point value; otherwise, it is constrained + // to be an integer. + // + NSCharacterSet *dot = + [NSCharacterSet characterSetWithCharactersInString:@"."]; + BOOL float_p = ([low rangeOfCharacterFromSet:dot].location != NSNotFound || + [high rangeOfCharacterFromSet:dot].location != NSNotFound); + if ([type isEqualToString:@"slider"]) { NSRect rect; rect.origin.x = rect.origin.y = 0; rect.size.width = 150; - rect.size.height = 20; + rect.size.height = 23; // apparent min height for slider with ticks... NSSlider *slider; if (cvt) slider = [[InvertedSlider alloc] initWithFrame:rect]; @@ -621,6 +633,31 @@ make_number_selector (NSUserDefaultsController *prefs, [slider setMaxValue:[high doubleValue]]; [slider setMinValue:[low doubleValue]]; + int range = [slider maxValue] - [slider minValue] + 1; + int range2 = range; + int max_ticks = 21; + while (range2 > max_ticks) + range2 /= 10; + + // If we have elided ticks, leave it at the max number of ticks. + if (range != range2 && range2 < max_ticks) + range2 = max_ticks; + + // If it's a float, always display the max number of ticks. + if (float_p && range2 < max_ticks) + range2 = max_ticks; + + [slider setNumberOfTickMarks:range2]; + + [slider setAllowsTickMarkValuesOnly: + (range == range2 && // we are showing the actual number of ticks + !float_p)]; // and we want integer results + + // #### Note: when the slider's range is large enough that we aren't + // showing all possible ticks, the slider's value is not constrained + // to be an integer, even though it should be... + // Maybe we need to use a value converter or something? + if (label) { NSTextField *lab = make_label (label); place_child (parent, lab, NO); @@ -711,8 +748,8 @@ make_number_selector (NSUserDefaultsController *prefs, [step sizeToFit]; place_child (parent, step, YES); rect = [step frame]; - rect.size.height = [txt frame].size.height; rect.origin.x -= COLUMN_SPACING; // this one goes close + rect.origin.y += ([txt frame].size.height - rect.size.height) / 2; [step setFrame:rect]; [step setMinValue:[low doubleValue]]; @@ -728,6 +765,18 @@ make_number_selector (NSUserDefaultsController *prefs, else [step setIncrement:1.0]; + NSNumberFormatter *fmt = [[[NSNumberFormatter alloc] init] autorelease]; + [fmt setFormatterBehavior:NSNumberFormatterBehavior10_4]; + [fmt setNumberStyle:NSNumberFormatterDecimalStyle]; + [fmt setMinimum:[NSNumber numberWithDouble:[low doubleValue]]]; + [fmt setMaximum:[NSNumber numberWithDouble:[high doubleValue]]]; + [fmt setMinimumFractionDigits: (float_p ? 1 : 0)]; + [fmt setMaximumFractionDigits: (float_p ? 2 : 0)]; + + [fmt setGeneratesDecimalNumbers:float_p]; + [[txt cell] setFormatter:fmt]; + + bind_switch_to_preferences (prefs, step, arg, opts); bind_switch_to_preferences (prefs, txt, arg, opts); @@ -791,6 +840,11 @@ make_option_menu (NSUserDefaultsController *prefs, rect.origin.x = rect.origin.y = 0; rect.size.width = 10; rect.size.height = 10; + + // #### "Build and Analyze" says that all of our widgets leak, because it + // seems to not realize that place_child -> addSubview retains them. + // Not sure what to do to make these warnings go away. + NSPopUpButton *popup = [[NSPopUpButton alloc] initWithFrame:rect pullsDown:NO]; @@ -805,7 +859,7 @@ make_option_menu (NSUserDefaultsController *prefs, if ([child kind] == NSXMLCommentKind) continue; if ([child kind] != NSXMLElementKind) { - NSAssert2 (0, @"weird XML node kind: %d: %@", [child kind], node); + NSAssert2 (0, @"weird XML node kind: %d: %@", (int)[child kind], node); continue; } @@ -997,6 +1051,69 @@ unwrap (NSString *text) } +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))) { + char *anchor = (char *) malloc (strlen(url) * 3 + 10); + strcpy (anchor, "Wikipedia: \""); + const char *in = url + strlen(wiki); + char *out = anchor + strlen(anchor); + while (*in) { + if (*in == '_') { + *out++ = ' '; + } else if (*in == '#') { + *out++ = ':'; + *out++ = ' '; + } else if (*in == '%') { + char hex[3]; + hex[0] = in[1]; + hex[1] = in[2]; + hex[2] = 0; + int n = 0; + sscanf (hex, "%x", &n); + *out++ = (char) n; + in += 2; + } else { + *out++ = *in; + } + in++; + } + *out++ = '"'; + *out = 0; + return anchor; + + } else if (!strncmp (math, url, strlen(math))) { + char *anchor = (char *) malloc (strlen(url) * 3 + 10); + strcpy (anchor, "MathWorld: \""); + const char *start = url + strlen(wiki); + const char *in = start; + char *out = anchor + strlen(anchor); + while (*in) { + if (*in == '_') { + *out++ = ' '; + } else if (in != start && *in >= 'A' && *in <= 'Z') { + *out++ = ' '; + *out++ = *in; + } else if (!strncmp (in, ".htm", 4)) { + break; + } else { + *out++ = *in; + } + in++; + } + *out++ = '"'; + *out = 0; + return anchor; + + } else { + return strdup (url); + } +} + + /* Converts any http: URLs in the given text field to clickable links. */ static void @@ -1040,15 +1157,24 @@ hreffify (NSText *nstext) NSString *nsurl = [text substringWithRange:r2]; const char *url = [nsurl UTF8String]; - // Construct the RTF corresponding to url + // If this is a Wikipedia URL, make the linked text be prettier. + // + char *anchor = anchorize(url); + + // Construct the RTF corresponding to anchor // const char *fmt = "{\\field{\\*\\fldinst{HYPERLINK \"%s\"}}%s}"; - char *rtf = malloc (strlen (fmt) + (strlen (url) * 2) + 10); - sprintf (rtf, fmt, url, url); + char *rtf = malloc (strlen (fmt) + strlen(url) + strlen(anchor) + 10); + sprintf (rtf, fmt, url, anchor); + free (anchor); NSData *rtfdata = [NSData dataWithBytesNoCopy:rtf length:strlen(rtf)]; // Insert the RTF into the NSText. [nstext replaceCharactersInRange:r2 withRTF:rtfdata]; + + int L2 = [text length]; // might have changed + start.location -= (L - L2); + L = L2; } } @@ -1120,6 +1246,8 @@ layout_group (NSView *group, BOOL horiz_p) } NSRect rect; + rect.origin.x = 0; + rect.origin.y = 0; rect.size.width = maxx; rect.size.height = -miny; [group setFrame:rect]; @@ -1147,22 +1275,26 @@ make_text_controls (NSUserDefaultsController *prefs, { /* Display Text: - (x) Computer Name and Time + (x) Computer name and time ( ) Text [__________________________] ( ) Text file [_________________] [Choose] ( ) URL [__________________________] + ( ) Shell Cmd [__________________________] textMode -text-mode date textMode -text-mode literal textLiteral -text-literal % textMode -text-mode file textFile -text-file % textMode -text-mode url textURL -text-url % + textMode -text-mode program textProgram -text-program % */ NSRect rect; rect.size.width = rect.size.height = 1; rect.origin.x = rect.origin.y = 0; - NSView *group = [[NSView alloc] initWithFrame:rect]; + NSView *group = [[NSView alloc] initWithFrame:rect]; NSView *rgroup = [[NSView alloc] initWithFrame:rect]; + Bool program_p = TRUE; + NSXMLElement *node2; NSView *control; @@ -1178,15 +1310,16 @@ make_text_controls (NSUserDefaultsController *prefs, initWithFrame:rect mode:NSRadioModeMatrix prototype:proto - numberOfRows:4 + numberOfRows: 4 + (program_p ? 1 : 0) numberOfColumns:1]; [matrix setAllowsEmptySelection:NO]; NSArrayController *cnames = [[NSArrayController alloc] initWithContent:nil]; - [cnames addObject:@"Computer Name and Time"]; + [cnames addObject:@"Computer name and time"]; [cnames addObject:@"Text"]; [cnames addObject:@"File"]; [cnames addObject:@"URL"]; + if (program_p) [cnames addObject:@"Shell Cmd"]; [matrix bind:@"content" toObject:cnames withKeyPath:@"arrangedObjects" @@ -1208,7 +1341,7 @@ make_text_controls (NSUserDefaultsController *prefs, make_text_field (prefs, opts, rgroup, node2, YES); [node2 release]; - rect = [last_child(rgroup) frame]; +// rect = [last_child(rgroup) frame]; /* // trying to make the text fields be enabled only when the checkbox is on.. control = last_child (rgroup); @@ -1226,10 +1359,10 @@ make_text_controls (NSUserDefaultsController *prefs, @"textFile", @"id", @"-text-file %", @"arg", nil]]; - make_file_selector (prefs, opts, rgroup, node2, NO, YES); + make_file_selector (prefs, opts, rgroup, node2, NO, YES, NO); [node2 release]; - rect = [last_child(rgroup) frame]; +// rect = [last_child(rgroup) frame]; // node2 = [[NSXMLElement alloc] initWithName:@"string"]; @@ -1241,7 +1374,21 @@ make_text_controls (NSUserDefaultsController *prefs, make_text_field (prefs, opts, rgroup, node2, YES); [node2 release]; - rect = [last_child(rgroup) frame]; +// rect = [last_child(rgroup) frame]; + + if (program_p) { + // + node2 = [[NSXMLElement alloc] initWithName:@"string"]; + [node2 setAttributesAsDictionary: + [NSDictionary dictionaryWithObjectsAndKeys: + @"textProgram", @"id", + @"-text-program %", @"arg", + nil]]; + make_text_field (prefs, opts, rgroup, node2, YES); + [node2 release]; + } + +// rect = [last_child(rgroup) frame]; layout_group (rgroup, NO); @@ -1255,6 +1402,8 @@ make_text_controls (NSUserDefaultsController *prefs, control = last_child (rgroup); rect = [control frame]; rect.size.width = 30; // width of the string "Text", plus a bit... + if (program_p) + rect.size.width += 25; rect.size.height += LINE_SPACING; [matrix setCellSize:rect.size]; [matrix sizeToCells]; @@ -1269,7 +1418,7 @@ make_text_controls (NSUserDefaultsController *prefs, // the text fields. // rect.size = [matrix cellSize]; - rect.size.width *= 10; + rect.size.width = 300; [matrix setCellSize:rect.size]; [matrix sizeToCells]; @@ -1301,13 +1450,13 @@ make_image_controls (NSUserDefaultsController *prefs, NSView *parent, NSXMLNode *node) { /* - [x] Grab Desktop Images - [ ] Choose Random Image: + [x] Grab desktop images + [ ] Choose random image: [__________________________] [Choose] - - */ @@ -1318,7 +1467,7 @@ make_image_controls (NSUserDefaultsController *prefs, [node2 setAttributesAsDictionary: [NSDictionary dictionaryWithObjectsAndKeys: @"grabDesktopImages", @"id", - @"Grab Desktop Images", @"_label", + @"Grab desktop images", @"_label", @"-no-grab-desktop", @"arg-unset", nil]]; make_checkbox (prefs, opts, parent, node2); @@ -1328,7 +1477,7 @@ make_image_controls (NSUserDefaultsController *prefs, [node2 setAttributesAsDictionary: [NSDictionary dictionaryWithObjectsAndKeys: @"chooseRandomImages", @"id", - @"Choose Random Images", @"_label", + @"Choose random images", @"_label", @"-choose-random-images", @"arg-set", nil]]; make_checkbox (prefs, opts, parent, node2); @@ -1338,11 +1487,24 @@ make_image_controls (NSUserDefaultsController *prefs, [node2 setAttributesAsDictionary: [NSDictionary dictionaryWithObjectsAndKeys: @"imageDirectory", @"id", - @"Images Directory:", @"_label", + @"Images from:", @"_label", @"-image-directory %", @"arg", nil]]; - make_file_selector (prefs, opts, parent, node2, YES, NO); + make_file_selector (prefs, opts, parent, node2, YES, NO, YES); [node2 release]; + + // Add a second, explanatory label below the file/URL selector. + + NSTextField *lab2 = 0; + lab2 = make_label (@"(Local folder, or URL of RSS or Atom feed)"); + place_child (parent, lab2, NO); + + // Pack it in a little tighter vertically. + NSRect r2 = [lab2 frame]; + r2.origin.x += 20; + r2.origin.y += 14; + [lab2 setFrameOrigin:r2.origin]; + [lab2 release]; } @@ -1358,7 +1520,7 @@ make_control (NSUserDefaultsController *prefs, if ([node kind] == NSXMLCommentKind) return; if ([node kind] != NSXMLElementKind) { - NSAssert2 (0, @"weird XML node kind: %d: %@", [node kind], node); + NSAssert2 (0, @"weird XML node kind: %d: %@", (int)[node kind], node); return; } @@ -1378,7 +1540,7 @@ make_control (NSUserDefaultsController *prefs, make_text_field (prefs, opts, parent, node, NO); } else if ([name isEqualToString:@"file"]) { - make_file_selector (prefs, opts, parent, node, NO, NO); + make_file_selector (prefs, opts, parent, node, NO, NO, NO); } else if ([name isEqualToString:@"number"]) { make_number_selector (prefs, opts, parent, node); @@ -1451,8 +1613,7 @@ fix_contentview_size (NSView *parent) NSRect f; NSArray *kids = [parent subviews]; int nkids = [kids count]; - NSView *text; // the NSText at the bottom of the window - NSView *last; // the last child before the NSText + NSView *text = 0; // the NSText at the bottom of the window double maxx = 0, miny = 0; int i; @@ -1468,7 +1629,6 @@ fix_contentview_size (NSView *parent) f = [kid frame]; if (f.origin.x + f.size.width > maxx) maxx = f.origin.x + f.size.width; if (f.origin.y - f.size.height < miny) miny = f.origin.y; - last = kid; // NSLog(@"start: %3.0f x %3.0f @ %3.0f %3.0f %3.0f %@", // f.size.width, f.size.height, f.origin.x, f.origin.y, // f.origin.y + f.size.height, [kid class]); @@ -1479,6 +1639,7 @@ fix_contentview_size (NSView *parent) /* Now that we know the width of the window, set the width of the NSText to that, so that it can decide what its height needs to be. */ + if (! text) abort(); f = [text frame]; // NSLog(@"text old: %3.0f x %3.0f @ %3.0f %3.0f %3.0f %@", // f.size.width, f.size.height, f.origin.x, f.origin.y, @@ -1512,6 +1673,10 @@ fix_contentview_size (NSView *parent) f = [text frame]; float dh = f.size.height - oh; f.origin.y += dh; + + // #### This is needed in OSX 10.5, but is wrong in OSX 10.6. WTF?? + // If we do this in 10.6, the text field moves down, off the window. + // So instead we repair it at the end, at the "WTF2" comment. [text setFrame:f]; // Also adjust the parent height by the change in height of the text field. @@ -1525,10 +1690,10 @@ fix_contentview_size (NSView *parent) /* Set the contentView to the size of the children. */ f = [parent frame]; - float yoff = f.size.height; +// float yoff = f.size.height; f.size.width = maxx + LEFT_MARGIN; f.size.height = -(miny - LEFT_MARGIN*2); - yoff = f.size.height - yoff; +// yoff = f.size.height - yoff; [parent setFrame:f]; // NSLog(@"max: %3.0f x %3.0f @ %3.0f %3.0f", @@ -1549,6 +1714,25 @@ fix_contentview_size (NSView *parent) // f.origin.y + f.size.height, [kid class]); } +/* +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 +*/ + + // #### 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. + // + f = [text frame]; + if (f.origin.y < 50) { // magic numbers, yay + f.origin.y = 50; + [text setFrame:f]; + } + /* Set the kids to track the top left corner of the window when resized. Set the NSText to track the bottom right corner as well. */ @@ -1654,9 +1838,16 @@ wrap_with_buttons (NSWindow *window, NSView *panel) [pbox setTitlePosition:NSNoTitle]; [pbox setBorderType:NSBezelBorder]; + // Enforce a max height on the dialog, so that it's obvious to me + // (on a big screen) when the dialog will fall off the bottom of + // a small screen (e.g., 1024x768 laptop with a huge bottom dock). { NSRect f = [panel frame]; - int screen_height = 800 - 64; + int screen_height = (768 // shortest "modern" Mac display + - 22 // menu bar + - 56 // System Preferences toolbar + - 140 // default magnified bottom dock icon + ); if (f.size.height > screen_height) { NSLog(@"%@ height was %.0f; clipping to %d", [panel class], f.size.height, screen_height); @@ -1749,13 +1940,6 @@ traverse_tree (NSUserDefaultsController *prefs, options:(NSXMLNodePreserveWhitespace | NSXMLNodePreserveCDATA) error:&err]; -/* clean up? - if (!xmlDoc) { - xmlDoc = [[NSXMLDocument alloc] initWithContentsOfURL:furl - options:NSXMLDocumentTidyXML - error:&err]; - } -*/ if (!xmlDoc || err) { if (err) NSAssert2 (0, @"XML Error: %@: %@", @@ -1764,6 +1948,7 @@ traverse_tree (NSUserDefaultsController *prefs, } traverse_tree (prefs, self, opts, [xmlDoc rootElement]); + [xmlDoc release]; return self; }