http://www.jwz.org/xscreensaver/xscreensaver-5.12.tar.gz
[xscreensaver] / OSX / XScreenSaverConfigSheet.m
index 24ab1c42558c3acfb950d8afbf8b84779192bf81..b529daddcaa31e2cdd4523039785441883f5a687 100644 (file)
@@ -1,4 +1,4 @@
-/* xscreensaver, Copyright (c) 2006 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 2006-2009 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
@@ -59,7 +59,7 @@ switch_to_resource (NSString *cmdline_switch, const XrmOptionDescRec *opts,
   NSAssert(cmdline_switch, @"cmdline switch is null");
   if (! [cmdline_switch getCString:buf maxLength:sizeof(buf)
                           encoding:NSUTF8StringEncoding]) {
-    NSAssert1(0, @"unable to convert %@\n", cmdline_switch);
+    NSAssert1(0, @"unable to convert %@", cmdline_switch);
     abort();
   }
   char *s = strpbrk(buf, " \t\r\n");
@@ -76,12 +76,12 @@ switch_to_resource (NSString *cmdline_switch, const XrmOptionDescRec *opts,
 
       if (opts[0].argKind == XrmoptionNoArg) {
         if (tail && *tail)
-          NSAssert1 (0, @"expected no args to switch: \"%@\"\n",
+          NSAssert1 (0, @"expected no args to switch: \"%@\"",
                      cmdline_switch);
         ret = opts[0].value;
       } else {
         if (!tail || !*tail)
-          NSAssert1 (0, @"expected args to switch: \"%@\"\n",
+          NSAssert1 (0, @"expected args to switch: \"%@\"",
                      cmdline_switch);
         ret = tail;
       }
@@ -101,7 +101,7 @@ switch_to_resource (NSString *cmdline_switch, const XrmOptionDescRec *opts,
     opts++;
   }
   
-  NSAssert1 (0, @"\"%@\" not present in options\n", cmdline_switch);
+  NSAssert1 (0, @"\"%@\" not present in options", cmdline_switch);
   abort();
 }
 
@@ -130,7 +130,7 @@ bind_resource_to_preferences (NSUserDefaultsController *prefs,
   s = [s stringByPaddingToLength:18 withString:@" " startingAtIndex:0];
   s = [NSString stringWithFormat:@"%@ = \"%@\"", s, def];
   s = [s stringByPaddingToLength:28 withString:@" " startingAtIndex:0];
-  NSLog (@"%@ %@/%@\n", s, [def class], [control class]);
+  NSLog (@"%@ %@/%@", s, [def class], [control class]);
 # endif
 }
 
@@ -170,9 +170,9 @@ parse_attrs (NSMutableDictionary *dict, NSXMLNode *node)
     NSString *old = [dict objectForKey:key];
     
     if (! old) {
-      NSAssert2 (0, @"unknown attribute \"%@\" in \"%@\"\n", key, [node name]);
+      NSAssert2 (0, @"unknown attribute \"%@\" in \"%@\"", key, [node name]);
     } else if ([old length] != 0) {
-      NSAssert2 (0, @"duplicate %@: \"%@\", \"%@\"\n", old, val);
+      NSAssert2 (0, @"duplicate %@: \"%@\", \"%@\"", old, val);
     } else {
       [dict setValue:val forKey:key];
     }
@@ -272,15 +272,15 @@ make_checkbox (NSUserDefaultsController *prefs,
   NSString *arg_unset = [dict objectForKey:@"arg-unset"];
   
   if (!label) {
-    NSAssert1 (0, @"no _label in %@\n", [node name]);
+    NSAssert1 (0, @"no _label in %@", [node name]);
     return;
   }
   if (!arg_set && !arg_unset) {
-    NSAssert1 (0, @"neither arg-set nor arg-unset provided in \"%@\"\n", 
+    NSAssert1 (0, @"neither arg-set nor arg-unset provided in \"%@\"", 
                label);
   }
   if (arg_set && arg_unset) {
-    NSAssert1 (0, @"only one of arg-set and arg-unset may be used in \"%@\"\n", 
+    NSAssert1 (0, @"only one of arg-set and arg-unset may be used in \"%@\"", 
                label);
   }
   
@@ -288,11 +288,11 @@ make_checkbox (NSUserDefaultsController *prefs,
   //
   if (arg_set && ([arg_set hasPrefix:@"-no-"] ||
                   [arg_set hasPrefix:@"--no-"]))
-    NSLog (@"arg-set should not be a \"no\" option in \"%@\": %@\n",
+    NSLog (@"arg-set should not be a \"no\" option in \"%@\": %@",
            label, arg_set);
   if (arg_unset && (![arg_unset hasPrefix:@"-no-"] &&
                     ![arg_unset hasPrefix:@"--no-"]))
-    NSLog(@"arg-unset should be a \"no\" option in \"%@\": %@\n",
+    NSLog(@"arg-unset should be a \"no\" option in \"%@\": %@",
           label, arg_unset);
     
   NSRect rect;
@@ -332,11 +332,11 @@ make_text_field (NSUserDefaultsController *prefs,
   NSString *arg   = [dict objectForKey:@"arg"];
 
   if (!label && !no_label_p) {
-    NSAssert1 (0, @"no _label in %@\n", [node name]);
+    NSAssert1 (0, @"no _label in %@", [node name]);
     return;
   }
 
-  NSAssert1 (arg, @"no arg in %@\n", label);
+  NSAssert1 (arg, @"no arg in %@", label);
 
   NSRect rect;
   rect.origin.x = rect.origin.y = 0;    
@@ -387,11 +387,11 @@ make_file_selector (NSUserDefaultsController *prefs,
   NSString *arg   = [dict objectForKey:@"arg"];
 
   if (!label && !no_label_p) {
-    NSAssert1 (0, @"no _label in %@\n", [node name]);
+    NSAssert1 (0, @"no _label in %@", [node name]);
     return;
   }
 
-  NSAssert1 (arg, @"no arg in %@\n", label);
+  NSAssert1 (arg, @"no arg in %@", label);
 
   NSRect rect;
   rect.origin.x = rect.origin.y = 0;    
@@ -483,7 +483,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];
 
@@ -586,19 +586,19 @@ make_number_selector (NSUserDefaultsController *prefs,
   NSString *def        = [dict objectForKey:@"default"];
   NSString *cvt        = [dict objectForKey:@"convert"];
   
-  NSAssert1 (arg,  @"no arg in %@\n", label);
-  NSAssert1 (type, @"no type in %@\n", label);
+  NSAssert1 (arg,  @"no arg in %@", label);
+  NSAssert1 (type, @"no type in %@", label);
 
   if (! low) {
-    NSAssert1 (0, @"no low in %@\n", [node name]);
+    NSAssert1 (0, @"no low in %@", [node name]);
     return;
   }
   if (! high) {
-    NSAssert1 (0, @"no high in %@\n", [node name]);
+    NSAssert1 (0, @"no high in %@", [node name]);
     return;
   }
   if (! def) {
-    NSAssert1 (0, @"no default in %@\n", [node name]);
+    NSAssert1 (0, @"no default in %@", [node name]);
     return;
   }
   if (cvt && ![cvt isEqualToString:@"invert"]) {
@@ -606,12 +606,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 +630,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);
@@ -665,14 +699,14 @@ make_number_selector (NSUserDefaultsController *prefs,
   } else if ([type isEqualToString:@"spinbutton"]) {
 
     if (! label) {
-      NSAssert1 (0, @"no _label in spinbutton %@\n", [node name]);
+      NSAssert1 (0, @"no _label in spinbutton %@", [node name]);
       return;
     }
     NSAssert1 (!low_label,
-              @"low-label not allowed in spinbutton \"%@\"\n", [node name]);
+              @"low-label not allowed in spinbutton \"%@\"", [node name]);
     NSAssert1 (!high_label,
-               @"high-label not allowed in spinbutton \"%@\"\n", [node name]);
-    NSAssert1 (!cvt, @"convert not allowed in spinbutton \"%@\"\n",
+               @"high-label not allowed in spinbutton \"%@\"", [node name]);
+    NSAssert1 (!cvt, @"convert not allowed in spinbutton \"%@\"",
                [node name]);
     
     NSRect rect;
@@ -711,8 +745,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 +762,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);
     
@@ -735,7 +781,7 @@ make_number_selector (NSUserDefaultsController *prefs,
     [txt release];
     
   } else {
-    NSAssert2 (0, @"unknown type \"%@\" in \"%@\"\n", type, label);
+    NSAssert2 (0, @"unknown type \"%@\" in \"%@\"", type, label);
   }
 }
 
@@ -775,7 +821,7 @@ make_option_menu (NSUserDefaultsController *prefs,
   int i, count = [children count];
 
   if (count <= 0) {
-    NSAssert1 (0, @"no menu items in \"%@\"\n", [node name]);
+    NSAssert1 (0, @"no menu items in \"%@\"", [node name]);
     return;
   }
 
@@ -791,6 +837,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];
 
@@ -822,7 +873,7 @@ make_option_menu (NSUserDefaultsController *prefs,
     NSString *arg_set = [dict2 objectForKey:@"arg-set"];
     
     if (!label) {
-      NSAssert1 (0, @"no _label in %@\n", [child name]);
+      NSAssert1 (0, @"no _label in %@", [child name]);
       return;
     }
 
@@ -836,7 +887,7 @@ make_option_menu (NSUserDefaultsController *prefs,
       NSAssert1 (this_val, @"this_val null for %@", arg_set);
       if (menu_key && ![menu_key isEqualToString:this_key])
         NSAssert3 (0,
-                   @"multiple resources in menu: \"%@\" vs \"%@\" = \"%@\"\n",
+                   @"multiple resources in menu: \"%@\" vs \"%@\" = \"%@\"",
                    menu_key, this_key, this_val);
       if (this_key)
         menu_key = this_key;
@@ -848,7 +899,7 @@ make_option_menu (NSUserDefaultsController *prefs,
 
     } else {
       // no arg-set -- only one menu item can be missing that.
-      NSAssert1 (!def_item, @"no arg-set in \"%@\"\n", label);
+      NSAssert1 (!def_item, @"no arg-set in \"%@\"", label);
       def_item = item;
     }
 
@@ -997,6 +1048,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,27 +1154,35 @@ hreffify (NSText *nstext)
     NSString *nsurl = [text substringWithRange:r2];
     const char *url = [nsurl UTF8String];
 
-    // Construct the RTF corresponding to <A HREF="url">url</A>
+    // If this is a Wikipedia URL, make the linked text be prettier.
+    //
+    char *anchor = anchorize(url);
+
+    // Construct the RTF corresponding to <A HREF="url">anchor</A>
     //
     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;
   }
 }
 
-/* Makes the first word of the text be bold.
+/* Makes the text up to the first comma be bold.
  */
 static void
 boldify (NSText *nstext)
 {
   NSString *text = [nstext string];
-  NSRange r = [text rangeOfCharacterFromSet:
-                      [NSCharacterSet whitespaceCharacterSet]];
-  r.length = r.location;
+  NSRange r = [text rangeOfString:@"," options:0];
+  r.length = r.location+1;
   r.location = 0;
 
   NSFont *font = [nstext font];
@@ -1148,7 +1270,7 @@ make_text_controls (NSUserDefaultsController *prefs,
 {
   /*
     Display Text:
-     (x)  Computer Name and Time
+     (x)  Computer name and time
      ( )  Text       [__________________________]
      ( )  Text file  [_________________] [Choose]
      ( )  URL        [__________________________]
@@ -1184,7 +1306,7 @@ make_text_controls (NSUserDefaultsController *prefs,
   [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"];
@@ -1209,7 +1331,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);
@@ -1230,7 +1352,7 @@ make_text_controls (NSUserDefaultsController *prefs,
   make_file_selector (prefs, opts, rgroup, node2, NO, YES);
   [node2 release];
 
-  rect = [last_child(rgroup) frame];
+//  rect = [last_child(rgroup) frame];
 
   //  <string id="textURL" _label="" arg-set="text-url %"/>
   node2 = [[NSXMLElement alloc] initWithName:@"string"];
@@ -1242,7 +1364,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];
 
   layout_group (rgroup, NO);
 
@@ -1302,13 +1424,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]
 
-   <boolean id="grabDesktopImages" _label="Grab Desktop Images"
+   <boolean id="grabDesktopImages" _label="Grab desktop images"
        arg-unset="-no-grab-desktop"/>
-   <boolean id="chooseRandomImages" _label="Grab Desktop Images"
+   <boolean id="chooseRandomImages" _label="Grab desktop images"
        arg-unset="-choose-random-images"/>
    <file id="imageDirectory" _label="" arg-set="-image-directory %"/>
    */
@@ -1319,7 +1441,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);
@@ -1329,7 +1451,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);
@@ -1339,7 +1461,7 @@ make_image_controls (NSUserDefaultsController *prefs,
   [node2 setAttributesAsDictionary:
           [NSDictionary dictionaryWithObjectsAndKeys:
                         @"imageDirectory",     @"id",
-                        @"Images Directory:",  @"_label",
+                        @"Images directory:",  @"_label",
                         @"-image-directory %", @"arg",
                         nil]];
   make_file_selector (prefs, opts, parent, node2, YES, NO);
@@ -1397,7 +1519,7 @@ make_control (NSUserDefaultsController *prefs,
     make_image_controls (prefs, opts, parent, node);
 
   } else {
-    NSAssert1 (0, @"unknown tag: %@\n", name);
+    NSAssert1 (0, @"unknown tag: %@", name);
   }
 }
 
@@ -1432,11 +1554,11 @@ parse_xscreensaver_tag (NSXMLNode *node)
   NSString *label = [dict objectForKey:@"_label"];
     
   if (!label) {
-    NSAssert1 (0, @"no _label in %@\n", [node name]);
+    NSAssert1 (0, @"no _label in %@", [node name]);
     return;
   }
   if (!name) {
-    NSAssert1 (0, @"no name in \"%@\"\n", label);
+    NSAssert1 (0, @"no name in \"%@\"", label);
     return;
   }
   
@@ -1452,8 +1574,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;
 
@@ -1469,17 +1590,17 @@ 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]);
   }
   
-  if (maxx < 350) maxx = 350;   // leave room for the NSText paragraph...
+  if (maxx < 400) maxx = 400;   // leave room for the NSText paragraph...
   
   /* 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,
@@ -1495,10 +1616,33 @@ fix_contentview_size (NSView *parent)
   miny = f.origin.y - LINE_SPACING;
   [text setFrame:f];
   
-  // Stop second-guessing us on sizing now.  Size is now locked.
+  // Lock the width of the field and unlock the height, and let it resize
+  // once more, to compute the proper height of the text for that width.
+  //
   [(NSText *) text setHorizontallyResizable:NO];
+  [(NSText *) text setVerticallyResizable:YES];
+  [(NSText *) text sizeToFit];
+
+  // Now lock the height too: no more resizing this text field.
+  //
   [(NSText *) text setVerticallyResizable:NO];
-  
+
+  // Now reposition the top edge of the text field to be back where it
+  // was before we changed the height.
+  //
+  float oh = f.size.height;
+  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.
+  miny -= dh;
+
 //  NSLog(@"text new: %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, [text class]);
@@ -1507,10 +1651,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", 
@@ -1531,6 +1675,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.
    */
@@ -1634,7 +1797,26 @@ wrap_with_buttons (NSWindow *window, NSView *panel)
   rect.origin.y += rect.size.height;
   NSBox *pbox = [[NSBox alloc] initWithFrame:rect];
   [pbox setTitlePosition:NSNoTitle];
-  [pbox setBorderType:NSNoBorder];
+  [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 = (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);
+      f.size.height = screen_height;
+      [panel setFrame:f];
+    }
+  }
+
   [pbox addSubview:panel];
   [pbox addSubview:bbox];
   [pbox sizeToFit];
@@ -1709,7 +1891,7 @@ traverse_tree (NSUserDefaultsController *prefs,
   NSURL *furl = [NSURL fileURLWithPath:xml_file];
 
   if (!furl) {
-    NSAssert1 (0, @"can't URLify \"%@\"\n", xml_file);
+    NSAssert1 (0, @"can't URLify \"%@\"", xml_file);
     return nil;
   }
 
@@ -1719,21 +1901,15 @@ 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: %@: %@\n",
+      NSAssert2 (0, @"XML Error: %@: %@",
                  xml_file, [err localizedDescription]);
     return nil;
   }
 
   traverse_tree (prefs, self, opts, [xmlDoc rootElement]);
+  [xmlDoc release];
 
   return self;
 }