summaryrefslogtreecommitdiffstats
path: root/OSX/XScreenSaverConfigSheet.m
diff options
context:
space:
mode:
Diffstat (limited to 'OSX/XScreenSaverConfigSheet.m')
-rw-r--r--OSX/XScreenSaverConfigSheet.m3710
1 files changed, 0 insertions, 3710 deletions
diff --git a/OSX/XScreenSaverConfigSheet.m b/OSX/XScreenSaverConfigSheet.m
deleted file mode 100644
index eaa5add..0000000
--- a/OSX/XScreenSaverConfigSheet.m
+++ /dev/null
@@ -1,3710 +0,0 @@
-/* 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
- * the above copyright notice appear in all copies and that both that
- * copyright notice and this permission notice appear in supporting
- * documentation. No representations are made about the suitability of this
- * software for any purpose. It is provided "as is" without express or
- * implied warranty.
- */
-
-/* XScreenSaver uses XML files to describe the user interface for configuring
- the various screen savers. These files live in .../hacks/config/ and
- say relatively high level things like: "there should be a checkbox
- labelled "Leave Trails", and when it is checked, add the option '-trails'
- to the command line when launching the program."
-
- This code reads that XML and constructs a Cocoa interface from it.
- The Cocoa controls are hooked up to NSUserDefaultsController to save
- those settings into the MacOS preferences system. The Cocoa preferences
- names are the same as the resource names specified in the screenhack's
- 'options' array (we use that array to map the command line switches
- specified in the XML to the resource names to use).
- */
-
-#import "XScreenSaverConfigSheet.h"
-#import "Updater.h"
-
-#import "jwxyz.h"
-#import "InvertedSlider.h"
-
-#ifdef USE_IPHONE
-# define NSView UIView
-# define NSRect CGRect
-# define NSSize CGSize
-# define NSTextField UITextField
-# define NSButton UIButton
-# define NSFont UIFont
-# define NSStepper UIStepper
-# define NSMenuItem UIMenuItem
-# define NSText UILabel
-# define minValue minimumValue
-# define maxValue maximumValue
-# define setMinValue setMinimumValue
-# define setMaxValue setMaximumValue
-# define LABEL UILabel
-#else
-# define LABEL NSTextField
-#endif // USE_IPHONE
-
-#undef LABEL_ABOVE_SLIDER
-#define USE_HTML_LABELS
-
-
-#pragma mark XML Parser
-
-/* I used to use the "NSXMLDocument" XML parser, but that doesn't exist
- on iOS. The "NSXMLParser" parser exists on both OSX and iOS, so I
- converted to use that. However, to avoid having to re-write all of
- the old code, I faked out a halfassed implementation of the
- "NSXMLNode" class that "NSXMLDocument" used to return.
- */
-
-#define NSXMLNode SimpleXMLNode
-#define NSXMLElement SimpleXMLNode
-#define NSXMLCommentKind SimpleXMLCommentKind
-#define NSXMLElementKind SimpleXMLElementKind
-#define NSXMLAttributeKind SimpleXMLAttributeKind
-#define NSXMLTextKind SimpleXMLTextKind
-
-typedef enum { SimpleXMLCommentKind,
- SimpleXMLElementKind,
- SimpleXMLAttributeKind,
- SimpleXMLTextKind,
-} SimpleXMLKind;
-
-@interface SimpleXMLNode : NSObject
-{
- SimpleXMLKind kind;
- NSString *name;
- SimpleXMLNode *parent;
- NSMutableArray *children;
- NSMutableArray *attributes;
- id object;
-}
-
-@property(nonatomic) SimpleXMLKind kind;
-@property(nonatomic, retain) NSString *name;
-@property(nonatomic, retain) SimpleXMLNode *parent;
-@property(nonatomic, retain) NSMutableArray *children;
-@property(nonatomic, retain) NSMutableArray *attributes;
-@property(nonatomic, retain, getter=objectValue, setter=setObjectValue:)
- id object;
-
-@end
-
-@implementation SimpleXMLNode
-
-@synthesize kind;
-@synthesize name;
-//@synthesize parent;
-@synthesize children;
-@synthesize attributes;
-@synthesize object;
-
-- (id) init
-{
- self = [super init];
- attributes = [NSMutableArray arrayWithCapacity:10];
- return self;
-}
-
-
-- (id) initWithName:(NSString *)n
-{
- self = [self init];
- [self setKind:NSXMLElementKind];
- [self setName:n];
- return self;
-}
-
-
-- (void) setAttributesAsDictionary:(NSDictionary *)dict
-{
- for (NSString *key in dict) {
- NSObject *val = [dict objectForKey:key];
- SimpleXMLNode *n = [[SimpleXMLNode alloc] init];
- [n setKind:SimpleXMLAttributeKind];
- [n setName:key];
- [n setObjectValue:val];
- [attributes addObject:n];
- [n release];
- }
-}
-
-- (SimpleXMLNode *) parent { return parent; }
-
-- (void) setParent:(SimpleXMLNode *)p
-{
- NSAssert (!parent, @"parent already set");
- if (!p) return;
- parent = p;
- NSMutableArray *kids = [p children];
- if (!kids) {
- kids = [NSMutableArray arrayWithCapacity:10];
- [p setChildren:kids];
- }
- [kids addObject:self];
-}
-@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.
- I can't believe Apple actually released that thing on the world.
- Let's fake up some radio buttons instead.
- */
-
-#if defined(USE_IPHONE) && !defined(USE_PICKER_VIEW)
-
-@interface RadioButton : UILabel
-{
- int index;
- NSArray *items;
-}
-
-@property(nonatomic) int index;
-@property(nonatomic, retain) NSArray *items;
-
-@end
-
-@implementation RadioButton
-
-@synthesize index;
-@synthesize items;
-
-- (id) initWithIndex:(int)_index items:_items
-{
- self = [super initWithFrame:CGRectZero];
- index = _index;
- items = [_items retain];
-
- [self setText: [[items objectAtIndex:index] objectAtIndex:0]];
- [self setBackgroundColor:[UIColor clearColor]];
- [self sizeToFit];
-
- return self;
-}
-
-@end
-
-
-# endif // !USE_PICKER_VIEW
-
-
-# pragma mark Implementing labels with clickable links
-
-#if defined(USE_IPHONE) && defined(USE_HTML_LABELS)
-
-@interface HTMLLabel : UIView <UIWebViewDelegate>
-{
- NSString *html;
- UIFont *font;
- UIWebView *webView;
-}
-
-@property(nonatomic, retain) NSString *html;
-@property(nonatomic, retain) UIWebView *webView;
-
-- (id) initWithHTML:(NSString *)h font:(UIFont *)f;
-- (id) initWithText:(NSString *)t font:(UIFont *)f;
-- (void) setHTML:(NSString *)h;
-- (void) setText:(NSString *)t;
-- (void) sizeToFit;
-
-@end
-
-@implementation HTMLLabel
-
-@synthesize html;
-@synthesize webView;
-
-- (id) initWithHTML:(NSString *)h font:(UIFont *)f
-{
- self = [super init];
- if (! self) return 0;
- font = [f retain];
- webView = [[UIWebView alloc] init];
- webView.delegate = self;
- webView.dataDetectorTypes = UIDataDetectorTypeNone;
- 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;
-}
-
-- (id) initWithText:(NSString *)t font:(UIFont *)f
-{
- self = [self initWithHTML:@"" font:f];
- if (! self) return 0;
- [self setText: t];
- return self;
-}
-
-
-- (void) setHTML: (NSString *)h
-{
- if (! h) return;
- [h retain];
- if (html) [html release];
- html = h;
- NSString *h2 =
- [NSString stringWithFormat:
- @"<!DOCTYPE HTML PUBLIC "
- "\"-//W3C//DTD HTML 4.01 Transitional//EN\""
- " \"http://www.w3.org/TR/html4/loose.dtd\">"
- "<HTML>"
- "<HEAD>"
-// "<META NAME=\"viewport\" CONTENT=\""
-// "width=device-width"
-// "initial-scale=1.0;"
-// "maximum-scale=1.0;\">"
- "<STYLE>"
- "<!--\n"
- "body {"
- " margin: 0; padding: 0; border: 0;"
- " 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>"
- "</HEAD>"
- "<BODY>"
- "%@"
- "</BODY>"
- "</HTML>",
- [font fontName],
- [font pointSize],
- [font lineHeight],
- h];
- [webView stopLoading];
- [webView loadHTMLString:h2 baseURL:[NSURL URLWithString:@""]];
-}
-
-
-static char *anchorize (const char *url);
-
-- (void) setText: (NSString *)t
-{
- t = [t stringByTrimmingCharactersInSet:[NSCharacterSet
- whitespaceCharacterSet]];
- t = [t stringByReplacingOccurrencesOfString:@"&" withString:@"&amp;"];
- t = [t stringByReplacingOccurrencesOfString:@"<" withString:@"&lt;"];
- t = [t stringByReplacingOccurrencesOfString:@">" withString:@"&gt;"];
- t = [t stringByReplacingOccurrencesOfString:@"\n\n" withString:@" <P> "];
- t = [t stringByReplacingOccurrencesOfString:@"<P> "
- withString:@"<P> &nbsp; &nbsp; &nbsp; &nbsp; "];
- t = [t stringByReplacingOccurrencesOfString:@"\n "
- withString:@"<BR> &nbsp; &nbsp; &nbsp; &nbsp; "];
-
- NSString *h = @"";
- for (NSString *s in
- [t componentsSeparatedByCharactersInSet:
- [NSCharacterSet whitespaceAndNewlineCharacterSet]]) {
- if ([s hasPrefix:@"http://"] ||
- [s hasPrefix:@"https://"]) {
- char *anchor = anchorize ([s cStringUsingEncoding:NSUTF8StringEncoding]);
- NSString *a2 = [NSString stringWithCString: anchor
- encoding: NSUTF8StringEncoding];
- s = [NSString stringWithFormat: @"<A HREF=\"%@\">%@</A><BR>", s, a2];
- free (anchor);
- }
- 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];
-}
-
-
--(BOOL) webView:(UIWebView *)wv
- shouldStartLoadWithRequest:(NSURLRequest *)req
- navigationType:(UIWebViewNavigationType)type
-{
- // Force clicked links to open in Safari, not in this window.
- if (type == UIWebViewNavigationTypeLinkClicked) {
- [[UIApplication sharedApplication] openURL:[req URL]];
- return NO;
- }
- return YES;
-}
-
-
-- (void) setFrame: (CGRect)r
-{
- [super setFrame: r];
- r.origin.x = 0;
- r.origin.y = 0;
- [webView setFrame: r];
-}
-
-
-- (NSString *) stripTags:(NSString *)str
-{
- NSString *result = @"";
-
- // Add newlines.
- str = [str stringByReplacingOccurrencesOfString:@"<P>"
- withString:@"<BR><BR>"
- options:NSCaseInsensitiveSearch
- range:NSMakeRange(0, [str length])];
- str = [str stringByReplacingOccurrencesOfString:@"<BR>"
- withString:@"\n"
- 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;
-}
-
-
-- (void) sizeToFit
-{
- CGRect r = [self frame];
-
- /* 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 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
- stringByEvaluatingJavaScriptFromString:
- @"document.body.offsetHeight"]
- doubleValue];
-# else
- NSString *text = [self stripTags: html];
- CGSize s = r.size;
- s.height = 999999;
- s = [text boundingRectWithSize:s
- options:NSStringDrawingUsesLineFragmentOrigin
- attributes:@{NSFontAttributeName: font}
- context:nil].size;
- r.size.height = s.height;
-# endif
-
- [self setFrame: r];
-}
-
-
-- (void) dealloc
-{
- [html release];
- [font release];
- [webView release];
- [super dealloc];
-}
-
-@end
-
-#endif // USE_IPHONE && USE_HTML_LABELS
-
-
-@interface XScreenSaverConfigSheet (Private)
-
-- (void)traverseChildren:(NSXMLNode *)node on:(NSView *)parent;
-
-# ifndef USE_IPHONE
-- (void) placeChild: (NSView *)c on:(NSView *)p right:(BOOL)r;
-- (void) placeChild: (NSView *)c on:(NSView *)p;
-static NSView *last_child (NSView *parent);
-static void layout_group (NSView *group, BOOL horiz_p);
-# else // USE_IPHONE
-- (void) placeChild: (NSObject *)c on:(NSView *)p right:(BOOL)r;
-- (void) placeChild: (NSObject *)c on:(NSView *)p;
-- (void) placeSeparator;
-- (void) bindResource:(NSObject *)ctl key:(NSString *)k reload:(BOOL)r;
-- (void) refreshTableView;
-# endif // USE_IPHONE
-
-@end
-
-
-@implementation XScreenSaverConfigSheet
-
-# define LEFT_MARGIN 20 // left edge of window
-# define COLUMN_SPACING 10 // gap between e.g. labels and text fields
-# define LEFT_LABEL_WIDTH 70 // width of all left labels
-# define LINE_SPACING 10 // leading between each line
-
-# define FONT_SIZE 17 // Magic hardcoded UITableView font size.
-
-#pragma mark Talking to the resource database
-
-
-/* Normally we read resources by looking up "KEY" in the database
- "org.jwz.xscreensaver.SAVERNAME". But in the all-in-one iPhone
- app, everything is stored in the database "org.jwz.xscreensaver"
- instead, so transform keys to "SAVERNAME.KEY".
-
- NOTE: This is duplicated in PrefsReader.m, cause I suck.
- */
-- (NSString *) makeKey:(NSString *)key
-{
-# ifdef USE_IPHONE
- NSString *prefix = [saver_name stringByAppendingString:@"."];
- if (! [key hasPrefix:prefix]) // Don't double up!
- key = [prefix stringByAppendingString:key];
-# endif
- return key;
-}
-
-
-- (NSString *) makeCKey:(const char *)key
-{
- return [self makeKey:[NSString stringWithCString:key
- encoding:NSUTF8StringEncoding]];
-}
-
-
-/* Given a command-line option, returns the corresponding resource name.
- Any arguments in the switch string are ignored (e.g., "-foo x").
- */
-- (NSString *) switchToResource:(NSString *)cmdline_switch
- opts:(const XrmOptionDescRec *)opts_array
- valRet:(NSString **)val_ret
-{
- char buf[1280];
- char *tail = 0;
- NSAssert(cmdline_switch, @"cmdline switch is null");
- if (! [cmdline_switch getCString:buf maxLength:sizeof(buf)
- encoding:NSUTF8StringEncoding]) {
- NSAssert1(0, @"unable to convert %@", cmdline_switch);
- return 0;
- }
- char *s = strpbrk(buf, " \t\r\n");
- if (s && *s) {
- *s = 0;
- tail = s+1;
- while (*tail && (*tail == ' ' || *tail == '\t'))
- tail++;
- }
-
- while (opts_array[0].option) {
- if (!strcmp (opts_array[0].option, buf)) {
- const char *ret = 0;
-
- if (opts_array[0].argKind == XrmoptionNoArg) {
- if (tail && *tail)
- NSAssert1 (0, @"expected no args to switch: \"%@\"",
- cmdline_switch);
- ret = opts_array[0].value;
- } else {
- if (!tail || !*tail)
- NSAssert1 (0, @"expected args to switch: \"%@\"",
- cmdline_switch);
- ret = tail;
- }
-
- if (val_ret)
- *val_ret = (ret
- ? [NSString stringWithCString:ret
- encoding:NSUTF8StringEncoding]
- : 0);
-
- const char *res = opts_array[0].specifier;
- while (*res && (*res == '.' || *res == '*'))
- res++;
- return [self makeCKey:res];
- }
- opts_array++;
- }
-
- NSAssert1 (0, @"\"%@\" not present in options", cmdline_switch);
- return 0;
-}
-
-
-- (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.
-//
-- (void)sliderAction:(UISlider*)sender
-{
- if ([active_text_field canResignFirstResponder])
- [active_text_field resignFirstResponder];
- NSString *pref_key = [pref_keys objectAtIndex: [sender tag]];
-
- // Hacky API. See comment in InvertedSlider.m.
- double v = ([sender isKindOfClass: [InvertedSlider class]]
- ? [(InvertedSlider *) sender transformedValue]
- : [sender value]);
-
- [[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.
-//
-- (void)switchAction:(UISwitch*)sender
-{
- if ([active_text_field canResignFirstResponder])
- [active_text_field resignFirstResponder];
- NSString *pref_key = [pref_keys objectAtIndex: [sender tag]];
- NSString *v = ([sender isOn] ? @"true" : @"false");
- [[self controllerForKey:pref_key] setObject:v forKey:pref_key];
-}
-
-# ifdef USE_PICKER_VIEW
-// Called when a picker is bonked.
-//
-- (void)pickerView:(UIPickerView *)pv
- didSelectRow:(NSInteger)row
- inComponent:(NSInteger)column
-{
- if ([active_text_field canResignFirstResponder])
- [active_text_field resignFirstResponder];
-
- NSAssert (column == 0, @"internal error");
- NSArray *a = [picker_values objectAtIndex: [pv tag]];
- if (! a) return; // Too early?
- a = [a objectAtIndex:row];
- NSAssert (a, @"missing row");
-
-//NSString *label = [a objectAtIndex:0];
- NSString *pref_key = [a objectAtIndex:1];
- NSObject *pref_val = [a objectAtIndex:2];
- [[self controllerForKey:pref_key] setObject:pref_val forKey:pref_key];
-}
-# else // !USE_PICKER_VIEW
-
-// Called when a RadioButton is bonked.
-//
-- (void)radioAction:(RadioButton*)sender
-{
- if ([active_text_field canResignFirstResponder])
- [active_text_field resignFirstResponder];
-
- NSArray *item = [[sender items] objectAtIndex: [sender index]];
- NSString *pref_key = [item objectAtIndex:1];
- NSObject *pref_val = [item objectAtIndex:2];
- [[self controllerForKey:pref_key] setObject:pref_val forKey:pref_key];
-}
-
-- (BOOL)textFieldShouldBeginEditing:(UITextField *)tf
-{
- active_text_field = tf;
- return YES;
-}
-
-- (void)textFieldDidEndEditing:(UITextField *)tf
-{
- NSString *pref_key = [pref_keys objectAtIndex: [tf tag]];
- NSString *txt = [tf text];
- [[self controllerForKey:pref_key] setObject:txt forKey:pref_key];
-}
-
-- (BOOL)textFieldShouldReturn:(UITextField *)tf
-{
- active_text_field = nil;
- [tf resignFirstResponder];
- return YES;
-}
-
-# endif // !USE_PICKER_VIEW
-
-#endif // USE_IPHONE
-
-
-# ifndef USE_IPHONE
-
-- (void) okAction:(NSObject *)arg
-{
- // 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];
- [globalDefaultsController revert:self];
- [NSApp endSheet:self returnCode:NSCancelButton];
- [self close];
-}
-# endif // !USE_IPHONE
-
-
-- (void) resetAction:(NSObject *)arg
-{
-# ifndef USE_IPHONE
- [userDefaultsController revertToInitialValues:self];
- [globalDefaultsController revertToInitialValues:self];
-# else // USE_IPHONE
-
- for (NSString *key in defaultOptions) {
- NSObject *val = [defaultOptions objectForKey:key];
- [[self controllerForKey:key] setObject:val forKey:key];
- }
-
- for (UIControl *ctl in pref_ctls) {
- NSString *pref_key = [pref_keys objectAtIndex: ctl.tag];
- [self bindResource:ctl key:pref_key reload:YES];
- }
-
- [self refreshTableView];
-# endif // USE_IPHONE
-}
-
-
-/* Connects a control (checkbox, etc) to the corresponding preferences key.
- */
-- (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:prefs
- withKeyPath:[@"values." stringByAppendingString: pref_key]
- options:opts_dict];
-
-# else // USE_IPHONE
- SEL sel;
- NSObject *val = [prefs objectForKey:pref_key];
- NSString *sval = 0;
- double dval = 0;
-
- if ([val isKindOfClass:[NSString class]]) {
- sval = (NSString *) val;
- if (NSOrderedSame == [sval caseInsensitiveCompare:@"true"] ||
- NSOrderedSame == [sval caseInsensitiveCompare:@"yes"] ||
- NSOrderedSame == [sval caseInsensitiveCompare:@"1"])
- dval = 1;
- else
- dval = [sval doubleValue];
- } else if ([val isKindOfClass:[NSNumber class]]) {
- // NSBoolean (__NSCFBoolean) is really NSNumber.
- dval = [(NSNumber *) val doubleValue];
- sval = [(NSNumber *) val stringValue];
- }
-
- if ([control isKindOfClass:[UISlider class]]) {
- sel = @selector(sliderAction:);
- // Hacky API. See comment in InvertedSlider.m.
- if ([control isKindOfClass:[InvertedSlider class]])
- [(InvertedSlider *) control setTransformedValue: dval];
- else
- [(UISlider *) control setValue: dval];
- } else if ([control isKindOfClass:[UISwitch class]]) {
- sel = @selector(switchAction:);
- [(UISwitch *) control setOn: ((int) dval != 0)];
-# ifdef USE_PICKER_VIEW
- } else if ([control isKindOfClass:[UIPickerView class]]) {
- sel = 0;
- [(UIPickerView *) control selectRow:((int)dval) inComponent:0
- animated:NO];
-# else // !USE_PICKER_VIEW
- } else if ([control isKindOfClass:[RadioButton class]]) {
- sel = 0; // radioAction: sent from didSelectRowAtIndexPath.
- } else if ([control isKindOfClass:[UITextField class]]) {
- sel = 0; // ####
- [(UITextField *) control setText: sval];
-# endif // !USE_PICKER_VIEW
- } else {
- NSAssert (0, @"unknown class");
- }
-
- // NSLog(@"\"%@\" = \"%@\" [%@, %.1f]", pref_key, val, [val class], dval);
-
- if (!reload_p) {
- if (! pref_keys) {
- pref_keys = [[NSMutableArray arrayWithCapacity:10] retain];
- pref_ctls = [[NSMutableArray arrayWithCapacity:10] retain];
- }
-
- [pref_keys addObject: [self makeKey:pref_key]];
- [pref_ctls addObject: control];
- ((UIControl *) control).tag = [pref_keys count] - 1;
-
- if (sel) {
- [(UIControl *) control addTarget:self action:sel
- forControlEvents:UIControlEventValueChanged];
- }
- }
-
-# endif // USE_IPHONE
-
-# if 0
- 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 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
-}
-
-
-- (void) bindResource:(NSObject *)control key:(NSString *)pref_key
-{
- [self bindResource:(NSObject *)control key:(NSString *)pref_key reload:NO];
-}
-
-
-
-- (void) bindSwitch:(NSObject *)control
- cmdline:(NSString *)cmd
-{
- [self bindResource:control
- key:[self switchToResource:cmd opts:opts valRet:0]];
-}
-
-
-#pragma mark Text-manipulating utilities
-
-
-static NSString *
-unwrap (NSString *text)
-{
- // Unwrap lines: delete \n but do not delete \n\n.
- //
- NSArray *lines = [text componentsSeparatedByString:@"\n"];
- NSUInteger i, nlines = [lines count];
- BOOL eolp = YES;
-
- text = @"\n"; // start with one blank line
-
- // skip trailing blank lines in file
- for (i = nlines-1; i > 0; i--) {
- NSString *s = (NSString *) [lines objectAtIndex:i];
- if ([s length] > 0)
- break;
- nlines--;
- }
-
- // skip leading blank lines in file
- for (i = 0; i < nlines; i++) {
- NSString *s = (NSString *) [lines objectAtIndex:i];
- if ([s length] > 0)
- break;
- }
-
- // unwrap
- Bool any = NO;
- for (; i < nlines; i++) {
- NSString *s = (NSString *) [lines objectAtIndex:i];
- if ([s length] == 0) {
- text = [text stringByAppendingString:@"\n\n"];
- 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.
- if (any && !eolp)
- text = [text stringByAppendingString:@"\n"];
- text = [text stringByAppendingString:s];
- any = YES;
- eolp = NO;
- } else {
- if (!eolp)
- text = [text stringByAppendingString:@" "];
- text = [text stringByAppendingString:s];
- eolp = NO;
- any = YES;
- }
- }
-
- return text;
-}
-
-
-# ifndef USE_IPHONE
-/* Makes the text up to the first comma be bold.
- */
-static void
-boldify (NSText *nstext)
-{
- NSString *text = [nstext string];
- NSRange r = [text rangeOfString:@"," options:0];
- r.length = r.location+1;
-
- r.location = 0;
-
- NSFont *font = [nstext font];
- font = [NSFont boldSystemFontOfSize:[font pointSize]];
- [nstext setFont:font range:r];
-}
-# endif // !USE_IPHONE
-
-
-/* Creates a human-readable anchor to put on a URL.
- */
-static char *
-anchorize (const char *url)
-{
- 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(!strncmp (wiki1, url, strlen(wiki1))
- ? wiki1 : wiki2);
- 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 (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(!strncmp (math1, url, strlen(math1))
- ? math1 : math2);
- 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);
- }
-}
-
-
-#if !defined(USE_IPHONE) || !defined(USE_HTML_LABELS)
-
-/* Converts any http: URLs in the given text field to clickable links.
- */
-static void
-hreffify (NSText *nstext)
-{
-# ifndef USE_IPHONE
- NSString *text = [nstext string];
- [nstext setRichText:YES];
-# else
- NSString *text = [nstext text];
-# endif
-
- NSUInteger L = [text length];
- NSRange start; // range is start-of-search to end-of-string
- start.location = 0;
- start.length = L;
- while (start.location < L) {
-
- // Find the beginning of a URL...
- //
- 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;
-
- // Next time around, start searching after this.
- start.location = r2.location + r2.length;
- start.length = L - start.location;
-
- // Find the end of a URL (whitespace or EOF)...
- //
- r3 = [text rangeOfCharacterFromSet:
- [NSCharacterSet whitespaceAndNewlineCharacterSet]
- options:0 range:start];
- if (r3.location == NSNotFound) // EOF
- r3.location = L, r3.length = 0;
-
- // Next time around, start searching after this.
- start.location = r3.location;
- start.length = L - start.location;
-
- // Set r2 to the start/length of this URL.
- r2.length = start.location - r2.location;
-
- // Extract the URL.
- NSString *nsurl = [text substringWithRange:r2];
- const char *url = [nsurl UTF8String];
-
- // If this is a Wikipedia URL, make the linked text be prettier.
- //
- char *anchor = anchorize(url);
-
-# ifndef USE_IPHONE
-
- // 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) + strlen(anchor) + 10);
- sprintf (rtf, fmt, url, anchor);
-
- NSData *rtfdata = [NSData dataWithBytesNoCopy:rtf length:strlen(rtf)];
- [nstext replaceCharactersInRange:r2 withRTF:rtfdata];
-
-# else // !USE_IPHONE
- // *anchor = 0; // Omit Wikipedia anchor
- text = [text stringByReplacingCharactersInRange:r2
- withString:[NSString stringWithCString:anchor
- encoding:NSUTF8StringEncoding]];
- // text = [text stringByReplacingOccurrencesOfString:@"\n\n\n"
- // withString:@"\n\n"];
-# endif // !USE_IPHONE
-
- free (anchor);
-
- NSUInteger L2 = [text length]; // might have changed
- start.location -= (L - L2);
- L = L2;
- }
-
-# ifdef USE_IPHONE
- [nstext setText:text];
- [nstext sizeToFit];
-# endif
-}
-
-#endif /* !USE_IPHONE || !USE_HTML_LABELS */
-
-
-
-#pragma mark Creating controls from XML
-
-
-/* Parse the attributes of an XML tag into a dictionary.
- For input, the dictionary should have as attributes the keys, each
- with @"" as their value.
- On output, the dictionary will set the keys to the values specified,
- and keys that were not specified will not be present in the dictionary.
- Warnings are printed if there are duplicate or unknown attributes.
- */
-- (void) parseAttrs:(NSMutableDictionary *)dict node:(NSXMLNode *)node
-{
- NSArray *attrs = [(NSXMLElement *) node attributes];
- NSUInteger n = [attrs count];
- int i;
-
- // For each key in the dictionary, fill in the dict with the corresponding
- // value. The value @"" is assumed to mean "un-set". Issue a warning if
- // an attribute is specified twice.
- //
- for (i = 0; i < n; i++) {
- NSXMLNode *attr = [attrs objectAtIndex:i];
- NSString *key = [attr name];
- NSString *val = [attr objectValue];
- NSString *old = [dict objectForKey:key];
-
- if (! old) {
- NSAssert2 (0, @"unknown attribute \"%@\" in \"%@\"", key, [node name]);
- } else if ([old length] != 0) {
- NSAssert3 (0, @"duplicate %@: \"%@\", \"%@\"", key, old, val);
- } else {
- [dict setValue:val forKey:key];
- }
- }
-
- // Remove from the dictionary any keys whose value is still @"",
- // meaning there was no such attribute specified.
- //
- NSArray *keys = [dict allKeys];
- n = [keys count];
- for (i = 0; i < n; i++) {
- NSString *key = [keys objectAtIndex:i];
- NSString *val = [dict objectForKey:key];
- if ([val length] == 0)
- [dict removeObjectForKey:key];
- }
-
-# ifdef USE_IPHONE
- // Kludge for starwars.xml:
- // If there is a "_low-label" and no "_label", but "_low-label" contains
- // spaces, divide them.
- NSString *lab = [dict objectForKey:@"_label"];
- NSString *low = [dict objectForKey:@"_low-label"];
- if (low && !lab) {
- NSArray *split =
- [[[low stringByTrimmingCharactersInSet:
- [NSCharacterSet whitespaceAndNewlineCharacterSet]]
- componentsSeparatedByString: @" "]
- filteredArrayUsingPredicate:
- [NSPredicate predicateWithFormat:@"length > 0"]];
- if (split && [split count] == 2) {
- [dict setValue:[split objectAtIndex:0] forKey:@"_label"];
- [dict setValue:[split objectAtIndex:1] forKey:@"_low-label"];
- }
- }
-# endif // USE_IPHONE
-}
-
-
-/* Handle the options on the top level <xscreensaver> tag.
- */
-- (NSString *) parseXScreenSaverTag:(NSXMLNode *)node
-{
- 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);
- return label;
-}
-
-
-/* Creates a label: an un-editable NSTextField displaying the given text.
- */
-- (LABEL *) makeLabel:(NSString *)text
-{
- NSRect rect;
- rect.origin.x = rect.origin.y = 0;
- rect.size.width = rect.size.height = 10;
-# ifndef USE_IPHONE
- NSTextField *lab = [[NSTextField alloc] initWithFrame:rect];
- [lab setSelectable:NO];
- [lab setEditable:NO];
- [lab setBezeled:NO];
- [lab setDrawsBackground:NO];
- [lab setStringValue:text];
- [lab sizeToFit];
-# else // USE_IPHONE
- UILabel *lab = [[UILabel alloc] initWithFrame:rect];
- [lab setText: [text stringByTrimmingCharactersInSet:
- [NSCharacterSet whitespaceAndNewlineCharacterSet]]];
- [lab setBackgroundColor:[UIColor clearColor]];
- [lab setNumberOfLines:0]; // unlimited
- // [lab setLineBreakMode:UILineBreakModeWordWrap];
- [lab setLineBreakMode:NSLineBreakByTruncatingHead];
- [lab setAutoresizingMask: (UIViewAutoresizingFlexibleWidth |
- UIViewAutoresizingFlexibleHeight)];
-# endif // USE_IPHONE
- [lab autorelease];
- return lab;
-}
-
-
-/* Creates the checkbox (NSButton) described by the given XML node.
- */
-- (void) makeCheckbox:(NSXMLNode *)node on:(NSView *)parent
-{
- 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]);
- return;
- }
- if (!arg_set && !arg_unset) {
- 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 \"%@\"",
- label);
- }
-
- // sanity-check the choice of argument names.
- //
- if (arg_set && ([arg_set hasPrefix:@"-no-"] ||
- [arg_set hasPrefix:@"--no-"]))
- 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 \"%@\": %@",
- label, arg_unset);
-
- NSRect rect;
- rect.origin.x = rect.origin.y = 0;
- rect.size.width = rect.size.height = 10;
-
-# ifndef USE_IPHONE
-
- NSButton *button = [[NSButton alloc] initWithFrame:rect];
- [button setButtonType:NSSwitchButton];
- [button setTitle:label];
- [button sizeToFit];
- [self placeChild:button on:parent];
-
-# else // USE_IPHONE
-
- LABEL *lab = [self makeLabel:label];
- [self placeChild:lab on:parent];
- UISwitch *button = [[UISwitch alloc] initWithFrame:rect];
- [self placeChild:button on:parent right:YES];
-
-# endif // USE_IPHONE
-
- [self bindSwitch:button cmdline:(arg_set ? arg_set : arg_unset)];
- [button release];
-}
-
-
-/* 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 = [@{ @"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_label = [dict objectForKey:@"_high-label"];
- NSString *type = [dict objectForKey:@"type"];
- NSString *arg = [dict objectForKey:@"arg"];
- NSString *low = [dict objectForKey:@"low"];
- 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);
-
- if (! low) {
- NSAssert1 (0, @"no low in %@", [node name]);
- return;
- }
- if (! high) {
- NSAssert1 (0, @"no high in %@", [node name]);
- return;
- }
- if (! def) {
- NSAssert1 (0, @"no default in %@", [node name]);
- return;
- }
- if (cvt && ![cvt isEqualToString:@"invert"]) {
- NSAssert1 (0, @"if provided, \"convert\" must be \"invert\" in %@",
- 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"]
-# ifdef USE_IPHONE // On iPhone, we use sliders for all numeric values.
- || [type isEqualToString:@"spinbutton"]
-# endif
- ) {
-
- NSRect rect;
- rect.origin.x = rect.origin.y = 0;
- rect.size.width = 150;
- rect.size.height = 23; // apparent min height for slider with ticks...
- NSSlider *slider;
- slider = [[InvertedSlider alloc] initWithFrame:rect
- inverted: !!cvt
- integers: !float_p];
- [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;
-
-# 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 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
-# endif // !USE_IPHONE
-
- // #### 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?
-
- LABEL *lab;
- if (label) {
- lab = [self makeLabel:label];
- [self placeChild:lab on:parent];
-# ifdef USE_IPHONE
- if (low_label) {
- CGFloat s = [NSFont systemFontSize] + 4;
- [lab setFont:[NSFont boldSystemFontOfSize:s]];
- }
-# endif
- }
-
- if (low_label) {
- lab = [self makeLabel:low_label];
- [lab setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]];
-# ifndef USE_IPHONE
- [lab setAlignment:1]; // right aligned
- rect = [lab frame];
- if (rect.size.width < LEFT_LABEL_WIDTH)
- rect.size.width = LEFT_LABEL_WIDTH; // make all left labels same size
- rect.size.height = [slider frame].size.height;
- [lab setFrame:rect];
- [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
- }
-
-# ifndef USE_IPHONE
- [self placeChild:slider on:parent right:(low_label ? YES : NO)];
-# else // USE_IPHONE
- [self placeChild:slider on:parent right:(label || low_label ? YES : NO)];
-# endif // USE_IPHONE
-
- if (low_label) {
- // Make left label be same height as slider.
- rect = [lab frame];
- rect.size.height = [slider frame].size.height;
- [lab setFrame:rect];
- }
-
- if (! low_label) {
- rect = [slider frame];
- if (rect.origin.x < LEFT_LABEL_WIDTH)
- rect.origin.x = LEFT_LABEL_WIDTH; // make unlabelled sliders line up too
- [slider setFrame:rect];
- }
-
- if (high_label) {
- lab = [self makeLabel:high_label];
- [lab setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]];
- rect = [lab frame];
-
- // 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];
- }
-
- [self bindSwitch:slider cmdline:arg];
- [slider release];
-
-#ifndef USE_IPHONE // On iPhone, we use sliders for all numeric values.
-
- } else if ([type isEqualToString:@"spinbutton"]) {
-
- if (! label) {
- NSAssert1 (0, @"no _label in spinbutton %@", [node name]);
- return;
- }
- NSAssert1 (!low_label,
- @"low-label not allowed in spinbutton \"%@\"", [node name]);
- NSAssert1 (!high_label,
- @"high-label not allowed in spinbutton \"%@\"", [node name]);
- NSAssert1 (!cvt, @"convert not allowed in spinbutton \"%@\"",
- [node name]);
-
- NSRect rect;
- rect.origin.x = rect.origin.y = 0;
- rect.size.width = rect.size.height = 10;
-
- NSTextField *txt = [[NSTextField alloc] initWithFrame:rect];
- [txt setStringValue:@"0000.0"];
- [txt sizeToFit];
- [txt setStringValue:@""];
-
- if (label) {
- LABEL *lab = [self makeLabel:label];
- //[lab setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]];
- [lab setAlignment:1]; // right aligned
- rect = [lab frame];
- if (rect.size.width < LEFT_LABEL_WIDTH)
- rect.size.width = LEFT_LABEL_WIDTH; // make all left labels same size
- rect.size.height = [txt frame].size.height;
- [lab setFrame:rect];
- [self placeChild:lab on:parent];
- }
-
- [self placeChild:txt on:parent right:(label ? YES : NO)];
-
- if (! label) {
- rect = [txt frame];
- if (rect.origin.x < LEFT_LABEL_WIDTH)
- rect.origin.x = LEFT_LABEL_WIDTH; // make unlabelled spinbtns line up
- [txt setFrame:rect];
- }
-
- rect.size.width = rect.size.height = 10;
- NSStepper *step = [[NSStepper alloc] initWithFrame:rect];
- [step sizeToFit];
- [self placeChild:step on:parent right:YES];
- rect = [step frame];
- 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]];
- [step setMaxValue:[high doubleValue]];
- [step setAutorepeat:YES];
- [step setValueWraps:NO];
-
- double range = [high doubleValue] - [low doubleValue];
- if (range < 1.0)
- [step setIncrement:range / 10.0];
- else if (range >= 500)
- [step setIncrement:range / 100.0];
- 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];
-
- [self bindSwitch:step cmdline:arg];
- [self bindSwitch:txt cmdline:arg];
-
- [step release];
- [txt release];
-
-# endif // USE_IPHONE
-
- } else {
- NSAssert2 (0, @"unknown type \"%@\" in \"%@\"", type, label);
- }
-}
-
-
-# ifndef USE_IPHONE
-static void
-set_menu_item_object (NSMenuItem *item, NSObject *obj)
-{
- /* If the object associated with this menu item looks like a boolean,
- store an NSNumber instead of an NSString, since that's what
- will be in the preferences (due to similar logic in PrefsReader).
- */
- if ([obj isKindOfClass:[NSString class]]) {
- NSString *string = (NSString *) obj;
- if (NSOrderedSame == [string caseInsensitiveCompare:@"true"] ||
- NSOrderedSame == [string caseInsensitiveCompare:@"yes"])
- obj = [NSNumber numberWithBool:YES];
- else if (NSOrderedSame == [string caseInsensitiveCompare:@"false"] ||
- NSOrderedSame == [string caseInsensitiveCompare:@"no"])
- obj = [NSNumber numberWithBool:NO];
- else
- obj = string;
- }
-
- [item setRepresentedObject:obj];
- //NSLog (@"menu item \"%@\" = \"%@\" %@", [item title], obj, [obj class]);
-}
-# endif // !USE_IPHONE
-
-
-/* Creates the popup menu described by the given XML node (and its children).
- */
-- (void) makeOptionMenu:(NSXMLNode *)node on:(NSView *)parent
-{
- NSArray *children = [node children];
- NSUInteger i, count = [children count];
-
- if (count <= 0) {
- NSAssert1 (0, @"no menu items in \"%@\"", [node name]);
- return;
- }
-
- // get the "id" attribute off the <select> tag.
- //
- NSMutableDictionary *dict = [@{ @"id": @"", } mutableCopy];
- [self parseAttrs:dict node:node];
- [dict release];
- dict = 0;
-
- NSRect rect;
- rect.origin.x = rect.origin.y = 0;
- rect.size.width = 10;
- rect.size.height = 10;
-
- NSString *menu_key = nil; // the resource key used by items in this menu
-
-# ifndef USE_IPHONE
- // #### "Build and Analyze" says that all of our widgets leak, because it
- // seems to not realize that placeChild -> addSubview retains them.
- // Not sure what to do to make these warnings go away.
-
- NSPopUpButton *popup = [[NSPopUpButton alloc] initWithFrame:rect
- pullsDown:NO];
- NSMenuItem *def_item = nil;
- float max_width = 0;
-
-# else // USE_IPHONE
-
- NSString *def_item = nil;
-
- rect.size.width = 0;
- rect.size.height = 0;
-# ifdef USE_PICKER_VIEW
- UIPickerView *popup = [[[UIPickerView alloc] initWithFrame:rect] retain];
- popup.delegate = self;
- popup.dataSource = self;
-# endif // !USE_PICKER_VIEW
- NSMutableArray *items = [NSMutableArray arrayWithCapacity:10];
-
-# endif // USE_IPHONE
-
- for (i = 0; i < count; i++) {
- NSXMLNode *child = [children objectAtIndex:i];
-
- if ([child kind] == NSXMLCommentKind)
- continue;
- if ([child kind] != NSXMLElementKind) {
-// NSAssert2 (0, @"weird XML node kind: %d: %@", (int)[child kind], node);
- continue;
- }
-
- // get the "id", "_label", and "arg-set" attrs off of the <option> tags.
- //
- 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]);
- continue;
- }
-
-# ifndef USE_IPHONE
- // create the menu item (and then get a pointer to it)
- [popup addItemWithTitle:label];
- NSMenuItem *item = [popup itemWithTitle:label];
-# endif // USE_IPHONE
-
- if (arg_set) {
- NSString *this_val = NULL;
- NSString *this_key = [self switchToResource: arg_set
- opts: opts
- valRet: &this_val];
- NSAssert1 (this_val, @"this_val null for %@", arg_set);
- if (menu_key && ![menu_key isEqualToString:this_key])
- NSAssert3 (0,
- @"multiple resources in menu: \"%@\" vs \"%@\" = \"%@\"",
- menu_key, this_key, this_val);
- if (this_key)
- menu_key = this_key;
-
- /* If this menu has the cmd line "-mode foo" then set this item's
- value to "foo" (the menu itself will be bound to e.g. "modeString")
- */
-# ifndef USE_IPHONE
- set_menu_item_object (item, this_val);
-# else
- // Array holds ["Label", "resource-key", "resource-val"].
- [items addObject:[NSMutableArray arrayWithObjects:
- label, @"", this_val, nil]];
-# endif
-
- } else {
- // no arg-set -- only one menu item can be missing that.
- NSAssert1 (!def_item, @"no arg-set in \"%@\"", label);
-# ifndef USE_IPHONE
- def_item = item;
-# else
- def_item = label;
- // Array holds ["Label", "resource-key", "resource-val"].
- [items addObject:[NSMutableArray arrayWithObjects:
- label, @"", @"", nil]];
-# endif
- }
-
- /* make sure the menu button has room for the text of this item,
- and remember the greatest width it has reached.
- */
-# ifndef USE_IPHONE
- [popup setTitle:label];
- [popup sizeToFit];
- NSRect r = [popup frame];
- if (r.size.width > max_width) max_width = r.size.width;
-# endif // USE_IPHONE
- }
-
- if (!menu_key) {
- NSAssert1 (0, @"no switches in menu \"%@\"", [dict objectForKey:@"id"]);
- return;
- }
-
- /* We've added all of the menu items. If there was an item with no
- command-line switch, then it's the item that represents the default
- value. Now we must bind to that item as well... (We have to bind
- this one late, because if it was the first item, then we didn't
- yet know what resource was associated with this menu.)
- */
- if (def_item) {
- NSObject *def_obj = [defaultOptions objectForKey:menu_key];
- NSAssert2 (def_obj,
- @"no default value for resource \"%@\" in menu item \"%@\"",
- menu_key,
-# ifndef USE_IPHONE
- [def_item title]
-# else
- def_item
-# endif
- );
-
-# ifndef USE_IPHONE
- set_menu_item_object (def_item, def_obj);
-# else // !USE_IPHONE
- for (NSMutableArray *a in items) {
- // Make sure each array contains the resource key.
- [a replaceObjectAtIndex:1 withObject:menu_key];
- // Make sure the default item contains the default resource value.
- if (def_obj && def_item &&
- [def_item isEqualToString:[a objectAtIndex:0]])
- [a replaceObjectAtIndex:2 withObject:def_obj];
- }
-# endif // !USE_IPHONE
- }
-
-# ifndef USE_IPHONE
-# ifdef USE_PICKER_VIEW
- /* Finish tweaking the menu button itself.
- */
- if (def_item)
- [popup setTitle:[def_item title]];
- NSRect r = [popup frame];
- r.size.width = max_width;
- [popup setFrame:r];
-# endif // USE_PICKER_VIEW
-# endif
-
-# if !defined(USE_IPHONE) || defined(USE_PICKER_VIEW)
- [self placeChild:popup on:parent];
- [self bindResource:popup key:menu_key];
- [popup release];
-# endif
-
-# ifdef USE_IPHONE
-# ifdef USE_PICKER_VIEW
- // Store the items for this picker in the picker_values array.
- // This is so fucking stupid.
-
- unsigned long menu_number = [pref_keys count] - 1;
- if (! picker_values)
- picker_values = [[NSMutableArray arrayWithCapacity:menu_number] retain];
- while ([picker_values count] <= menu_number)
- [picker_values addObject:[NSArray arrayWithObjects: nil]];
- [picker_values replaceObjectAtIndex:menu_number withObject:items];
- [popup reloadAllComponents];
-
-# else // !USE_PICKER_VIEW
-
- [self placeSeparator];
-
- i = 0;
- 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++;
- }
-
- [self placeSeparator];
-
-# endif // !USE_PICKER_VIEW
-# endif // !USE_IPHONE
-
-}
-
-
-/* Creates an uneditable, wrapping NSTextField to display the given
- text enclosed by <description> ... </description> in the XML.
- */
-- (void) makeDescLabel:(NSXMLNode *)node on:(NSView *)parent
-{
- NSString *text = nil;
- NSArray *children = [node children];
- NSUInteger i, count = [children count];
-
- for (i = 0; i < count; i++) {
- NSXMLNode *child = [children objectAtIndex:i];
- NSString *s = [child objectValue];
- if (text)
- text = [text stringByAppendingString:s];
- else
- text = s;
- }
-
- text = unwrap (text);
-
- NSRect rect = [parent frame];
- rect.origin.x = rect.origin.y = 0;
- rect.size.width = 200;
- 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];
- [lab setVerticallyResizable:YES];
- [lab setString:text];
- hreffify (lab);
- boldify (lab);
- [lab sizeToFit];
-
-# else // USE_IPHONE
-
-# ifndef USE_HTML_LABELS
-
- UILabel *lab = [self makeLabel:text];
- [lab setFont:[NSFont systemFontOfSize: [NSFont systemFontSize]]];
- hreffify (lab);
-
-# else // USE_HTML_LABELS
- HTMLLabel *lab = [[HTMLLabel alloc]
- initWithText:text
- font:[NSFont systemFontOfSize: [NSFont systemFontSize]]];
- [lab autorelease];
- [lab setFrame:rect];
- [lab sizeToFit];
-# endif // USE_HTML_LABELS
-
- [self placeSeparator];
-
-# endif // USE_IPHONE
-
- [self placeChild:lab on:parent];
-}
-
-
-/* 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 = [@{ @"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]);
- return;
- }
-
- NSAssert1 (arg, @"no arg in %@", label);
-
- NSRect rect;
- rect.origin.x = rect.origin.y = 0;
- rect.size.width = rect.size.height = 10;
-
- NSTextField *txt = [[NSTextField alloc] initWithFrame:rect];
-
-# ifndef USE_IPHONE
-
- // make the default size be around 30 columns; a typical value for
- // these text fields is "xscreensaver-text --cols 40".
- //
- [txt setStringValue:@"123456789 123456789 123456789 "];
- [txt sizeToFit];
- [[txt cell] setWraps:NO];
- [[txt cell] setScrollable:YES];
- [txt setStringValue:@""];
-
-# else // USE_IPHONE
-
- txt.adjustsFontSizeToFitWidth = YES;
- txt.textColor = [UIColor blackColor];
- txt.font = [UIFont systemFontOfSize: FONT_SIZE];
- txt.placeholder = @"";
- txt.borderStyle = UITextBorderStyleRoundedRect;
- txt.textAlignment = NSTextAlignmentRight;
- txt.keyboardType = UIKeyboardTypeDefault; // Full kbd
- txt.autocorrectionType = UITextAutocorrectionTypeNo;
- txt.autocapitalizationType = UITextAutocapitalizationTypeNone;
- txt.clearButtonMode = UITextFieldViewModeAlways;
- txt.returnKeyType = UIReturnKeyDone;
- txt.delegate = self;
- txt.text = @"";
- [txt setEnabled: YES];
-
- rect.size.height = [txt.font lineHeight] * 1.2;
- [txt setFrame:rect];
-
-# endif // USE_IPHONE
-
- if (label) {
- LABEL *lab = [self makeLabel:label];
- [self placeChild:lab on:parent];
- }
-
- [self placeChild:txt on:parent right:(label ? YES : NO)];
-
- [self bindSwitch:txt cmdline:arg];
- [txt release];
-}
-
-
-/* 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
- withLabel: (BOOL) label_p
- editable: (BOOL) editable_p
-{
-# ifndef USE_IPHONE // No files. No selectors.
- 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]);
- return;
- }
-
- NSAssert1 (arg, @"no arg in %@", label);
-
- NSRect rect;
- rect.origin.x = rect.origin.y = 0;
- rect.size.width = rect.size.height = 10;
-
- NSTextField *txt = [[NSTextField alloc] initWithFrame:rect];
-
- // make the default size be around 20 columns.
- //
- [txt setStringValue:@"123456789 123456789 "];
- [txt sizeToFit];
- [txt setSelectable:YES];
- [txt setEditable:editable_p];
- [txt setBezeled:editable_p];
- [txt setDrawsBackground:editable_p];
- [[txt cell] setWraps:NO];
- [[txt cell] setScrollable:YES];
- [[txt cell] setLineBreakMode:NSLineBreakByTruncatingHead];
- [txt setStringValue:@""];
-
- LABEL *lab = 0;
- if (label) {
- lab = [self makeLabel:label];
- [self placeChild:lab on:parent];
- }
-
- [self placeChild:txt on:parent right:(label ? YES : NO)];
-
- [self bindSwitch:txt cmdline:arg];
- [txt release];
-
- // 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 > [txt frame].size.height
- ? [lab frame].size.height
- : [txt frame].size.height);
- [txt setFrame:rect];
- }
-
- // Now put a "Choose" button next to it.
- //
- rect.origin.x = rect.origin.y = 0;
- rect.size.width = rect.size.height = 10;
- NSButton *choose = [[NSButton alloc] initWithFrame:rect];
- [choose setTitle:@"Choose..."];
- [choose setBezelStyle:NSRoundedBezelStyle];
- [choose sizeToFit];
-
- [self placeChild:choose on:parent right:YES];
-
- // center the Choose button around the midpoint of the text field.
- rect = [choose frame];
- rect.origin.y = ([txt frame].origin.y +
- (([txt frame].size.height - rect.size.height) / 2));
- [choose setFrameOrigin:rect.origin];
-
- [choose setTarget:[parent window]];
- if (dirsOnly)
- [choose setAction:@selector(fileSelectorChooseDirsAction:)];
- else
- [choose setAction:@selector(fileSelectorChooseAction:)];
-
- [choose release];
-# endif // !USE_IPHONE
-}
-
-
-# ifndef USE_IPHONE
-
-/* Runs a modal file selector and sets the text field's value to the
- selected file or directory.
- */
-static void
-do_file_selector (NSTextField *txt, BOOL dirs_p)
-{
- NSOpenPanel *panel = [NSOpenPanel openPanel];
- [panel setAllowsMultipleSelection:NO];
- [panel setCanChooseFiles:!dirs_p];
- [panel setCanChooseDirectories:dirs_p];
-
- NSInteger result = [panel runModal];
- if (result == NSOKButton) {
- NSArray *files = [panel URLs];
- NSString *file = ([files count] > 0 ? [[files objectAtIndex:0] path] : @"");
- file = [file stringByAbbreviatingWithTildeInPath];
- [txt setStringValue:file];
-
- // Fuck me! Just setting the value of the NSTextField does not cause
- // that to end up in the preferences!
- //
- NSDictionary *dict = [txt infoForBinding:@"value"];
- NSUserDefaultsController *prefs = [dict objectForKey:@"NSObservedObject"];
- NSString *path = [dict objectForKey:@"NSObservedKeyPath"];
- if ([path hasPrefix:@"values."]) // WTF.
- path = [path substringFromIndex:7];
- [[prefs values] setValue:file forKey:path];
- }
-}
-
-
-/* Returns the NSTextField that is to the left of or above the NSButton.
- */
-static NSTextField *
-find_text_field_of_button (NSButton *button)
-{
- NSView *parent = [button superview];
- NSArray *kids = [parent subviews];
- NSUInteger nkids = [kids count];
- int i;
- NSTextField *f = 0;
- for (i = 0; i < nkids; i++) {
- NSObject *kid = [kids objectAtIndex:i];
- if ([kid isKindOfClass:[NSTextField class]]) {
- f = (NSTextField *) kid;
- } else if (kid == button) {
- if (! f) abort();
- return f;
- }
- }
- abort();
-}
-
-
-- (void) fileSelectorChooseAction:(NSObject *)arg
-{
- NSButton *choose = (NSButton *) arg;
- NSTextField *txt = find_text_field_of_button (choose);
- do_file_selector (txt, NO);
-}
-
-- (void) fileSelectorChooseDirsAction:(NSObject *)arg
-{
- NSButton *choose = (NSButton *) arg;
- NSTextField *txt = find_text_field_of_button (choose);
- do_file_selector (txt, YES);
-}
-
-#endif // !USE_IPHONE
-
-
-- (void) makeTextLoaderControlBox:(NSXMLNode *)node on:(NSView *)parent
-{
-# ifndef USE_IPHONE
- /*
- Display Text:
- (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 *rgroup = [[NSView alloc] initWithFrame:rect];
-
- Bool program_p = TRUE;
-
-
- NSView *control;
-
- // This is how you link radio buttons together.
- //
- NSButtonCell *proto = [[NSButtonCell alloc] init];
- [proto setButtonType:NSRadioButton];
-
- rect.origin.x = rect.origin.y = 0;
- rect.size.width = rect.size.height = 10;
- NSMatrix *matrix = [[NSMatrix alloc]
- initWithFrame:rect
- mode:NSRadioModeMatrix
- prototype:proto
- 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:@"Text"];
- [cnames addObject:@"File"];
- [cnames addObject:@"URL"];
- if (program_p) [cnames addObject:@"Shell Cmd"];
- [matrix bind:@"content"
- toObject:cnames
- withKeyPath:@"arrangedObjects"
- options:nil];
- [cnames release];
-
- [self bindSwitch:matrix cmdline:@"-text-mode %"];
-
- [self placeChild:matrix on:group];
- [self placeChild:rgroup on:group right:YES];
- [proto release];
- [matrix release];
- [rgroup release];
-
- NSXMLNode *node2;
-
-# else // USE_IPHONE
-
- NSView *rgroup = parent;
- NSXMLNode *node2;
-
- // <select id="textMode">
- // <option id="date" _label="Display date" arg-set="-text-mode date"/>
- // <option id="text" _label="Display text" arg-set="-text-mode literal"/>
- // <option id="url" _label="Display URL"/>
- // </select>
-
- node2 = [[NSXMLElement alloc] initWithName:@"select"];
- [node2 setAttributesAsDictionary:@{ @"id": @"textMode" }];
-
- NSXMLNode *node3 = [[NSXMLElement alloc] initWithName:@"option"];
- [node3 setAttributesAsDictionary:
- @{ @"id": @"date",
- @"arg-set": @"-text-mode date",
- @"_label": @"Display the date and time" }];
- [node3 setParent: node2];
- [node3 autorelease];
-
- node3 = [[NSXMLElement alloc] initWithName:@"option"];
- [node3 setAttributesAsDictionary:
- @{ @"id": @"text",
- @"arg-set": @"-text-mode literal",
- @"_label": @"Display static text" }];
- [node3 setParent: node2];
- [node3 autorelease];
-
- node3 = [[NSXMLElement alloc] initWithName:@"option"];
- [node3 setAttributesAsDictionary:
- @{ @"id": @"url",
- @"_label": @"Display the contents of a URL" }];
- [node3 setParent: node2];
- [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:
- @{ @"id": @"textLiteral",
- @"arg": @"-text-literal %",
-# ifdef USE_IPHONE
- @"_label": @"Text to display"
-# endif
- }];
- [self makeTextField:node2 on:rgroup
-# ifndef USE_IPHONE
- withLabel:NO
-# else
- withLabel:YES
-# endif
- horizontal:NO];
- [node2 release];
-
-// rect = [last_child(rgroup) frame];
-
-/* // trying to make the text fields be enabled only when the checkbox is on..
- control = last_child (rgroup);
- [control bind:@"enabled"
- 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:
- @{ @"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:
- @{ @"id": @"textURL",
- @"arg": @"-text-url %",
-# ifdef USE_IPHONE
- @"_label": @"URL to display",
-# endif
- }];
- [self makeTextField:node2 on:rgroup
-# ifndef USE_IPHONE
- withLabel:NO
-# else
- withLabel:YES
-# endif
- horizontal:NO];
- [node2 release];
-
-// rect = [last_child(rgroup) frame];
-
-# ifndef USE_IPHONE
- if (program_p) {
- // <string id="textProgram" _label="" arg-set="text-program %"/>
- node2 = [[NSXMLElement alloc] initWithName:@"string"];
- [node2 setAttributesAsDictionary:
- @{ @"id": @"textProgram",
- @"arg": @"-text-program %",
- }];
- [self makeTextField:node2 on:rgroup withLabel:NO horizontal:NO];
- [node2 release];
- }
-
-// rect = [last_child(rgroup) frame];
-
- layout_group (rgroup, NO);
-
- rect = [rgroup frame];
- rect.size.width += 35; // WTF? Why is rgroup too narrow?
- [rgroup setFrame:rect];
-
-
- // Set the height of the cells in the radio-box matrix to the height of
- // the (last of the) text fields.
- 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];
-
- layout_group (group, YES);
- rect = [matrix frame];
- rect.origin.x += rect.size.width + COLUMN_SPACING;
- rect.origin.y -= [control frame].size.height - LINE_SPACING;
- [rgroup setFrameOrigin:rect.origin];
-
- // now cheat on the size of the matrix: allow it to overlap (underlap)
- // the text fields.
- //
- rect.size = [matrix cellSize];
- rect.size.width = 300;
- [matrix setCellSize:rect.size];
- [matrix sizeToCells];
-
- // Cheat on the position of the stuff on the right (the rgroup).
- // GAAAH, this code is such crap!
- rect = [rgroup frame];
- rect.origin.y -= 5;
- [rgroup setFrame:rect];
-
-
- rect.size.width = rect.size.height = 0;
- NSBox *box = [[NSBox alloc] initWithFrame:rect];
- [box setTitlePosition:NSAtTop];
- [box setBorderType:NSBezelBorder];
- [box setTitle:@"Display Text"];
-
- rect.size.width = rect.size.height = 12;
- [box setContentViewMargins:rect.size];
- [box setContentView:group];
- [box sizeToFit];
-
- [self placeChild:box on:parent];
- [group release];
- [box release];
-
-# endif // !USE_IPHONE
-}
-
-
-- (void) makeImageLoaderControlBox:(NSXMLNode *)node on:(NSView *)parent
-{
- /*
- [x] Grab desktop images
- [ ] Choose random image:
- [__________________________] [Choose]
-
- <boolean id="grabDesktopImages" _label="Grab desktop images"
- arg-unset="-no-grab-desktop"/>
- <boolean id="chooseRandomImages" _label="Grab desktop images"
- arg-unset="-choose-random-images"/>
- <file id="imageDirectory" _label="" arg-set="-image-directory %"/>
- */
-
- NSXMLElement *node2;
-
-# ifndef USE_IPHONE
-# define SCREENS "Grab desktop images"
-# define PHOTOS "Choose random images"
-# else
-# define SCREENS "Grab screenshots"
-# define PHOTOS "Use photo library"
-# endif
-
- node2 = [[NSXMLElement alloc] initWithName:@"boolean"];
- [node2 setAttributesAsDictionary:
- @{ @"id": @"grabDesktopImages",
- @"_label": @ SCREENS,
- @"arg-unset": @"-no-grab-desktop",
- }];
- [self makeCheckbox:node2 on:parent];
- [node2 release];
-
- node2 = [[NSXMLElement alloc] initWithName:@"boolean"];
- [node2 setAttributesAsDictionary:
- @{ @"id": @"chooseRandomImages",
- @"_label": @ PHOTOS,
- @"arg-set": @"-choose-random-images",
- }];
- [self makeCheckbox:node2 on:parent];
- [node2 release];
-
- node2 = [[NSXMLElement alloc] initWithName:@"string"];
- [node2 setAttributesAsDictionary:
- @{ @"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
-
-# ifndef USE_IPHONE
- // Add a second, explanatory label below the file/URL selector.
-
- LABEL *lab2 = 0;
- lab2 = [self makeLabel:@"(Local folder, or URL of RSS or Atom feed)"];
- [self placeChild:lab2 on:parent];
-
- // Pack it in a little tighter vertically.
- NSRect r2 = [lab2 frame];
- r2.origin.x += 20;
- r2.origin.y += 14;
- [lab2 setFrameOrigin:r2.origin];
-# 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
-
-
-# ifndef USE_IPHONE
-static NSView *
-last_child (NSView *parent)
-{
- NSArray *kids = [parent subviews];
- NSUInteger nkids = [kids count];
- if (nkids == 0)
- return 0;
- else
- return [kids objectAtIndex:nkids-1];
-}
-#endif // USE_IPHONE
-
-
-/* Add the child as a subview of the parent, positioning it immediately
- below or to the right of the previously-added child of that view.
- */
-- (void) placeChild:
-# ifdef USE_IPHONE
- (NSObject *)child
-# else
- (NSView *)child
-# endif
- on:(NSView *)parent right:(BOOL)right_p
-{
-# ifndef USE_IPHONE
- NSRect rect = [child frame];
- NSView *last = last_child (parent);
- if (!last) {
- rect.origin.x = LEFT_MARGIN;
- rect.origin.y = ([parent frame].size.height - rect.size.height
- - LINE_SPACING);
- } else if (right_p) {
- rect = [last frame];
- rect.origin.x += rect.size.width + COLUMN_SPACING;
- } else {
- rect = [last frame];
- rect.origin.x = LEFT_MARGIN;
- rect.origin.y -= [child frame].size.height + LINE_SPACING;
- }
- NSRect r = [child frame];
- r.origin = rect.origin;
- [child setFrame:r];
- [parent addSubview:child];
-
-# else // USE_IPHONE
-
- /* 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)
- [controls addObject: [NSMutableArray arrayWithCapacity:10]];
- NSMutableArray *current = [controls objectAtIndex:[controls count]-1];
-
- if (!right_p || [current count] == 0) {
- // Nothing on the current line. Add this object.
- [current addObject: child];
- } else {
- // Something's on the current line already.
- NSObject *old = [current objectAtIndex:[current count]-1];
- if ([old isKindOfClass:[NSMutableArray class]]) {
- // Already an array in this cell. Append.
- NSAssert ([(NSArray *) old count] < 4, @"internal error");
- [(NSMutableArray *) old addObject: child];
- } else {
- // 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];
- }
- }
-# endif // USE_IPHONE
-}
-
-
-- (void) placeChild:(NSView *)child on:(NSView *)parent
-{
- [self placeChild:child on:parent right:NO];
-}
-
-
-#ifdef USE_IPHONE
-
-// Start putting subsequent children in a new group, to create a new
-// section on the UITableView.
-//
-- (void) placeSeparator
-{
- if (! controls) return;
- if ([controls count] == 0) return;
- if ([[controls objectAtIndex:[controls count]-1]
- count] > 0)
- [controls addObject: [NSMutableArray arrayWithCapacity:10]];
-}
-#endif // USE_IPHONE
-
-
-
-/* Creates an invisible NSBox (for layout purposes) to enclose the widgets
- wrapped in <hgroup> or <vgroup> in the XML.
- */
-- (void) makeGroup:(NSXMLNode *)node
- on:(NSView *)parent
- horizontal:(BOOL) horiz_p
-{
-# ifdef USE_IPHONE
- if (!horiz_p) [self placeSeparator];
- [self traverseChildren:node on:parent];
- if (!horiz_p) [self placeSeparator];
-# else // !USE_IPHONE
- NSRect rect;
- rect.size.width = rect.size.height = 1;
- rect.origin.x = rect.origin.y = 0;
- NSView *group = [[NSView alloc] initWithFrame:rect];
- [self traverseChildren:node on:group];
-
- layout_group (group, horiz_p);
-
- 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
-}
-
-
-#ifndef USE_IPHONE
-static void
-layout_group (NSView *group, BOOL horiz_p)
-{
- NSArray *kids = [group subviews];
- NSUInteger nkids = [kids count];
- NSUInteger i;
- double maxx = 0, miny = 0;
- for (i = 0; i < nkids; i++) {
- NSView *kid = [kids objectAtIndex:i];
- NSRect r = [kid frame];
-
- if (horiz_p) {
- maxx += r.size.width + COLUMN_SPACING;
- if (r.size.height > -miny) miny = -r.size.height;
- } else {
- if (r.size.width > maxx) maxx = r.size.width;
- miny = r.origin.y - r.size.height;
- }
- }
-
- NSRect rect;
- rect.origin.x = 0;
- rect.origin.y = 0;
- rect.size.width = maxx;
- rect.size.height = -miny;
- [group setFrame:rect];
-
- double x = 0;
- for (i = 0; i < nkids; i++) {
- NSView *kid = [kids objectAtIndex:i];
- NSRect r = [kid frame];
- if (horiz_p) {
- r.origin.y = rect.size.height - r.size.height;
- r.origin.x = x;
- x += r.size.width + COLUMN_SPACING;
- } else {
- r.origin.y -= miny;
- }
- [kid setFrame:r];
- }
-}
-#endif // !USE_IPHONE
-
-
-/* Create some kind of control corresponding to the given XML node.
- */
--(void)makeControl:(NSXMLNode *)node on:(NSView *)parent
-{
- NSString *name = [node name];
-
- if ([node kind] == NSXMLCommentKind)
- return;
-
- if ([node kind] == NSXMLTextKind) {
- NSString *s = [(NSString *) [node objectValue]
- stringByTrimmingCharactersInSet:
- [NSCharacterSet whitespaceAndNewlineCharacterSet]];
- if (! [s isEqualToString:@""]) {
- NSAssert1 (0, @"unexpected text: %@", s);
- }
- return;
- }
-
- if ([node kind] != NSXMLElementKind) {
- NSAssert2 (0, @"weird XML node kind: %d: %@", (int)[node kind], node);
- return;
- }
-
- if ([name isEqualToString:@"hgroup"] ||
- [name isEqualToString:@"vgroup"]) {
-
- [self makeGroup:node on:parent
- horizontal:[name isEqualToString:@"hgroup"]];
-
- } 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:@"string"]) {
- [self makeTextField:node on:parent withLabel:NO horizontal:NO];
-
- } else if ([name isEqualToString:@"file"]) {
- [self makeFileSelector:node on:parent
- dirsOnly:NO withLabel:YES editable:NO];
-
- } else if ([name isEqualToString:@"number"]) {
- [self makeNumberSelector:node on:parent];
-
- } else if ([name isEqualToString:@"select"]) {
- [self makeOptionMenu:node on:parent];
-
- } else if ([name isEqualToString:@"_description"]) {
- [self makeDescLabel:node on:parent];
-
- } else if ([name isEqualToString:@"xscreensaver-text"]) {
- [self makeTextLoaderControlBox: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);
- }
-}
-
-
-/* Iterate over and process the children of this XML node.
- */
-- (void)traverseChildren:(NSXMLNode *)node on:(NSView *)parent
-{
- NSArray *children = [node children];
- NSUInteger i, count = [children count];
- for (i = 0; i < count; i++) {
- NSXMLNode *child = [children objectAtIndex:i];
- [self makeControl:child on:parent];
- }
-}
-
-
-# ifndef USE_IPHONE
-
-/* Kludgey magic to make the window enclose the controls we created.
- */
-static void
-fix_contentview_size (NSView *parent)
-{
- NSRect f;
- NSArray *kids = [parent subviews];
- NSUInteger nkids = [kids count];
- NSView *text = 0; // the NSText at the bottom of the window
- double maxx = 0, miny = 0;
- NSUInteger i;
-
- /* Find the size of the rectangle taken up by each of the children
- except the final "NSText" child.
- */
- for (i = 0; i < nkids; i++) {
- NSView *kid = [kids objectAtIndex:i];
- if ([kid isKindOfClass:[NSText class]]) {
- text = kid;
- continue;
- }
- 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;
-// 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 < 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,
-// f.origin.y + f.size.height, [text class]);
-
- // set the NSText's width (this changes its height).
- f.size.width = maxx - LEFT_MARGIN;
- [text setFrame:f];
-
- // position the NSText below the last child (this gives us a new miny).
- f = [text frame];
- f.origin.y = miny - f.size.height - LINE_SPACING;
- miny = f.origin.y - LINE_SPACING;
- [text setFrame:f];
-
- // 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]);
-
-
- /* Set the contentView to the size of the children.
- */
- f = [parent frame];
-// float yoff = f.size.height;
- f.size.width = maxx + LEFT_MARGIN;
- f.size.height = -(miny - LEFT_MARGIN*2);
-// yoff = f.size.height - yoff;
- [parent setFrame:f];
-
-// NSLog(@"max: %3.0f x %3.0f @ %3.0f %3.0f",
-// f.size.width, f.size.height, f.origin.x, f.origin.y);
-
- /* Now move all of the kids up into the window.
- */
- f = [parent frame];
- float shift = f.size.height;
-// NSLog(@"shift: %3.0f", shift);
- for (i = 0; i < nkids; i++) {
- NSView *kid = [kids objectAtIndex:i];
- f = [kid frame];
- f.origin.y += shift;
- [kid setFrame:f];
-// NSLog(@"move: %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]);
- }
-
-/*
- 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.
- */
- for (i = 0; i < nkids; i++) {
- NSView *kid = [kids objectAtIndex:i];
- unsigned long mask = NSViewMaxXMargin | NSViewMinYMargin;
- if ([kid isKindOfClass:[NSText class]])
- mask |= NSViewWidthSizable|NSViewHeightSizable;
- [kid setAutoresizingMask:mask];
- }
-}
-# endif // !USE_IPHONE
-
-
-
-#ifndef USE_IPHONE
-static NSView *
-wrap_with_buttons (NSWindow *window, NSView *panel)
-{
- NSRect rect;
-
- // Make a box to hold the buttons at the bottom of the window.
- //
- rect = [panel frame];
- rect.origin.x = rect.origin.y = 0;
- rect.size.height = 10;
- NSBox *bbox = [[NSBox alloc] initWithFrame:rect];
- [bbox setTitlePosition:NSNoTitle];
- [bbox setBorderType:NSNoBorder];
-
- // Make some buttons: Default, Cancel, OK
- //
- rect.origin.x = rect.origin.y = 0;
- rect.size.width = rect.size.height = 10;
- NSButton *reset = [[NSButton alloc] initWithFrame:rect];
- [reset setTitle:@"Reset to Defaults"];
- [reset setBezelStyle:NSRoundedBezelStyle];
- [reset sizeToFit];
-
- rect = [reset frame];
- NSButton *ok = [[NSButton alloc] initWithFrame:rect];
- [ok setTitle:@"OK"];
- [ok setBezelStyle:NSRoundedBezelStyle];
- [ok sizeToFit];
- rect = [bbox frame];
- rect.origin.x = rect.size.width - [ok frame].size.width;
- [ok setFrameOrigin:rect.origin];
-
- rect = [ok frame];
- NSButton *cancel = [[NSButton alloc] initWithFrame:rect];
- [cancel setTitle:@"Cancel"];
- [cancel setBezelStyle:NSRoundedBezelStyle];
- [cancel sizeToFit];
- rect.origin.x -= [cancel frame].size.width + 10;
- [cancel setFrameOrigin:rect.origin];
-
- // Bind OK to RET and Cancel to ESC.
- [ok setKeyEquivalent:@"\r"];
- [cancel setKeyEquivalent:@"\e"];
-
- // The correct width for OK and Cancel buttons is 68 pixels
- // ("Human Interface Guidelines: Controls: Buttons:
- // Push Button Specifications").
- //
- rect = [ok frame];
- rect.size.width = 68;
- [ok setFrame:rect];
-
- rect = [cancel frame];
- rect.size.width = 68;
- [cancel setFrame:rect];
-
- // It puts the buttons in the box or else it gets the hose again
- //
- [bbox addSubview:ok];
- [bbox addSubview:cancel];
- [bbox addSubview:reset];
- [bbox sizeToFit];
-
- // make a box to hold the button-box, and the preferences view
- //
- rect = [bbox frame];
- rect.origin.y += rect.size.height;
- NSBox *pbox = [[NSBox alloc] initWithFrame:rect];
- [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 = (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];
-
- [reset setAutoresizingMask:NSViewMaxXMargin];
- [cancel setAutoresizingMask:NSViewMinXMargin];
- [ok setAutoresizingMask:NSViewMinXMargin];
- [bbox setAutoresizingMask:NSViewWidthSizable];
-
- // grab the clicks
- //
- [ok setTarget:window];
- [cancel setTarget:window];
- [reset setTarget:window];
- [ok setAction:@selector(okAction:)];
- [cancel setAction:@selector(cancelAction:)];
- [reset setAction:@selector(resetAction:)];
-
- [bbox release];
-
- return pbox;
-}
-#endif // !USE_IPHONE
-
-
-/* Iterate over and process the children of the root node of the XML document.
- */
-- (void)traverseTree
-{
-# ifdef USE_IPHONE
- NSView *parent = [self view];
-# else
- NSWindow *parent = self;
-#endif
- NSXMLNode *node = xml_root;
-
- if (![[node name] isEqualToString:@"screensaver"]) {
- NSAssert (0, @"top level node is not <xscreensaver>");
- }
-
- saver_name = [self parseXScreenSaverTag: node];
- saver_name = [saver_name stringByReplacingOccurrencesOfString:@" "
- withString:@""];
- [saver_name retain];
-
-# ifndef USE_IPHONE
-
- NSRect rect;
- rect.origin.x = rect.origin.y = 0;
- rect.size.width = rect.size.height = 1;
-
- NSView *panel = [[NSView alloc] initWithFrame:rect];
- [self traverseChildren:node on:panel];
- fix_contentview_size (panel);
-
- NSView *root = wrap_with_buttons (parent, panel);
- [userDefaultsController setAppliesImmediately:NO];
- [globalDefaultsController setAppliesImmediately:NO];
-
- [panel setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
-
- rect = [parent frameRectForContentRect:[root frame]];
- [parent setFrame:rect display:NO];
- [parent setMinSize:rect.size];
-
- [parent setContentView:root];
-
- [panel release];
- [root release];
-
-# else // USE_IPHONE
-
- CGRect r = [parent frame];
- r.size = [[UIScreen mainScreen] bounds].size;
- [parent setFrame:r];
- [self traverseChildren:node on:parent];
-
-# endif // USE_IPHONE
-}
-
-
-- (void)parser:(NSXMLParser *)parser
- didStartElement:(NSString *)elt
- namespaceURI:(NSString *)ns
- qualifiedName:(NSString *)qn
- attributes:(NSDictionary *)attrs
-{
- NSXMLElement *e = [[NSXMLElement alloc] initWithName:elt];
- [e autorelease];
- [e setKind:SimpleXMLElementKind];
- [e setAttributesAsDictionary:attrs];
- NSXMLElement *p = xml_parsing;
- [e setParent:p];
- xml_parsing = e;
- if (! xml_root)
- xml_root = xml_parsing;
-}
-
-- (void)parser:(NSXMLParser *)parser
- didEndElement:(NSString *)elt
- namespaceURI:(NSString *)ns
- qualifiedName:(NSString *)qn
-{
- NSXMLElement *p = xml_parsing;
- if (! p) {
- NSLog(@"extra close: %@", elt);
- } else if (![[p name] isEqualToString:elt]) {
- NSLog(@"%@ closed by %@", [p name], elt);
- } else {
- NSXMLElement *n = xml_parsing;
- xml_parsing = [n parent];
- }
-}
-
-
-- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
-{
- NSXMLElement *e = [[NSXMLElement alloc] initWithName:@"text"];
- [e setKind:SimpleXMLTextKind];
- NSXMLElement *p = xml_parsing;
- [e setParent:p];
- [e setObjectValue: string];
- [e autorelease];
-}
-
-
-# ifdef USE_IPHONE
-# ifdef USE_PICKER_VIEW
-
-#pragma mark UIPickerView delegate methods
-
-- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pv
-{
- return 1; // Columns
-}
-
-- (NSInteger)pickerView:(UIPickerView *)pv
- numberOfRowsInComponent:(NSInteger)column
-{
- NSAssert (column == 0, @"weird column");
- NSArray *a = [picker_values objectAtIndex: [pv tag]];
- if (! a) return 0; // Too early?
- return [a count];
-}
-
-- (CGFloat)pickerView:(UIPickerView *)pv
- rowHeightForComponent:(NSInteger)column
-{
- return FONT_SIZE;
-}
-
-- (CGFloat)pickerView:(UIPickerView *)pv
- widthForComponent:(NSInteger)column
-{
- NSAssert (column == 0, @"weird column");
- NSArray *a = [picker_values objectAtIndex: [pv tag]];
- if (! a) return 0; // Too early?
-
- UIFont *f = [UIFont systemFontOfSize:[NSFont systemFontSize]];
- 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;
- }
-
- max *= 1.7; // WTF!!
-
- if (max > 320)
- max = 320;
- else if (max < 120)
- max = 120;
-
- return max;
-
-}
-
-
-- (NSString *)pickerView:(UIPickerView *)pv
- titleForRow:(NSInteger)row
- forComponent:(NSInteger)column
-{
- NSAssert (column == 0, @"weird column");
- NSArray *a = [picker_values objectAtIndex: [pv tag]];
- if (! a) return 0; // Too early?
- a = [a objectAtIndex:row];
- NSAssert (a, @"internal error");
- return [a objectAtIndex:0];
-}
-
-# endif // USE_PICKER_VIEW
-
-
-#pragma mark UITableView delegate methods
-
-- (void) addResetButton
-{
- [[self navigationItem]
- setRightBarButtonItem: [[UIBarButtonItem alloc]
- initWithTitle: @"Reset to Defaults"
- style: UIBarButtonItemStylePlain
- target:self
- action:@selector(resetAction:)]];
- NSString *s = saver_name;
- if ([self view].frame.size.width > 320)
- s = [s stringByAppendingString: @" Settings"];
- [self navigationItem].title = s;
-}
-
-
-- (BOOL)shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)o
-{
- return YES;
-}
-
-- (NSInteger)numberOfSectionsInTableView:(UITableView *)tv {
- // Number of vertically-stacked white boxes.
- return [controls count];
-}
-
-- (NSInteger)tableView:(UITableView *)tableView
- numberOfRowsInSection:(NSInteger)section
-{
- // Number of lines in each vertically-stacked white box.
- NSAssert (controls, @"internal error");
- return [[controls objectAtIndex:section] count];
-}
-
-- (NSString *)tableView:(UITableView *)tv
- titleForHeaderInSection:(NSInteger)section
-{
- // Titles above each vertically-stacked white box.
-// if (section == 0)
-// return [saver_name stringByAppendingString:@" Settings"];
- return nil;
-}
-
-
-- (CGFloat)tableView:(UITableView *)tv
- heightForRowAtIndexPath:(NSIndexPath *)ip
-{
- 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: // 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]]) {
- // 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;
- CGRect r = t.frame;
- r.size.width = [tv frame].size.width;
- r.size.width -= LEFT_MARGIN * 2;
- [t setFrame:r];
- [t sizeToFit];
- r = t.frame;
- h = r.size.height;
-# endif // USE_HTML_LABELS
-
- } else { // Does this ever happen?
- h = FONT_SIZE + LINE_SPACING * 2;
- }
-
- if (h <= 0) abort();
- return h;
-}
-
-
-- (void)refreshTableView
-{
- UITableView *tv = (UITableView *) [self view];
- NSMutableArray *a = [NSMutableArray arrayWithCapacity:20];
- NSInteger rows = [self numberOfSectionsInTableView:tv];
- for (int i = 0; i < rows; i++) {
- NSInteger cols = [self tableView:tv numberOfRowsInSection:i];
- for (int j = 0; j < cols; j++) {
- NSUInteger ip[2];
- ip[0] = i;
- ip[1] = j;
- [a addObject: [NSIndexPath indexPathWithIndexes:ip length:2]];
- }
- }
-
- [tv beginUpdates];
- [tv reloadRowsAtIndexPaths:a withRowAnimation:UITableViewRowAnimationNone];
- [tv endUpdates];
-
- // Default opacity looks bad.
- // #### Oh great, this only works *sometimes*.
- UIView *v = [[self navigationItem] titleView];
- [v setBackgroundColor:[[v backgroundColor] colorWithAlphaComponent:1]];
-}
-
-
-- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)o
-{
- [NSTimer scheduledTimerWithTimeInterval: 0
- target:self
- selector:@selector(refreshTableView)
- userInfo:nil
- repeats:NO];
-}
-
-
-#ifndef USE_PICKER_VIEW
-
-- (void)updateRadioGroupCell:(UITableViewCell *)cell
- button:(RadioButton *)b
-{
- NSArray *item = [[b items] objectAtIndex: [b index]];
- NSString *pref_key = [item objectAtIndex:1];
- NSObject *pref_val = [item objectAtIndex:2];
-
- NSObject *current = [[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".
- // Will boolean true/1 screw us here too?
- //
- NSString *pref_str = ([pref_val isKindOfClass:[NSString class]]
- ? (NSString *) pref_val
- : [(NSNumber *) pref_val stringValue]);
- NSString *current_str = ([current isKindOfClass:[NSString class]]
- ? (NSString *) current
- : [(NSNumber *) current stringValue]);
- BOOL match_p = [current_str isEqualToString:pref_str];
-
- // NSLog(@"\"%@\" = \"%@\" | \"%@\" ", pref_key, pref_val, current_str);
-
- if (match_p)
- [cell setAccessoryType:UITableViewCellAccessoryCheckmark];
- else
- [cell setAccessoryType:UITableViewCellAccessoryNone];
-}
-
-
-- (void)tableView:(UITableView *)tv
- didSelectRowAtIndexPath:(NSIndexPath *)ip
-{
- RadioButton *ctl = [[controls objectAtIndex:[ip indexAtPosition:0]]
- objectAtIndex:[ip indexAtPosition:1]];
- if (! [ctl isKindOfClass:[RadioButton class]])
- return;
-
- [self radioAction:ctl];
- [self refreshTableView];
-}
-
-
-#endif // !USE_PICKER_VIEW
-
-
-
-- (UITableViewCell *)tableView:(UITableView *)tv
- cellForRowAtIndexPath:(NSIndexPath *)ip
-{
- CGFloat ww = [tv frame].size.width;
- CGFloat hh = [self tableView:tv heightForRowAtIndexPath:ip];
-
- float os_version = [[[UIDevice currentDevice] systemVersion] floatValue];
-
- // Width of the column of labels on the left.
- CGFloat left_width = ww * 0.4;
- CGFloat right_edge = ww - 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]];
-
- if ([ctl isKindOfClass:[NSArray class]]) {
- // This cell has a set of objects in it.
- NSArray *set = (NSArray *) ctl;
- switch ([set count]) {
- case 2:
- {
- // With 2 elements, the first of the pair must be a label.
- UILabel *label = (UILabel *) [set objectAtIndex: 0];
- NSAssert ([label isKindOfClass:[UILabel class]], @"unhandled type");
- ctl = [set objectAtIndex: 1];
-
- CGRect r = [ctl frame];
-
- if ([ctl isKindOfClass:[UISwitch class]]) { // Checkboxes.
- r.size.width = 80; // Magic.
- 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 {
- r.origin.x = left_width; // Text fields, etc.
- r.size.width = right_edge - r.origin.x;
- }
-
- r.origin.y = (hh - r.size.height) / 2; // Center vertically.
- [ctl setFrame:r];
-
- // 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];
-
- // 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 = hh;
- [label setFrame:r];
- [label setFont:[NSFont boldSystemFontOfSize: FONT_SIZE]];
- [box addSubview: label];
- [box autorelease];
-
- ctl = box;
- }
- break;
- case 3:
- case 4:
- {
- // 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++]
- : 0);
- UILabel *left = [set objectAtIndex: i++];
- NSView *mid = [set objectAtIndex: i++];
- UILabel *right = [set objectAtIndex: i++];
- NSAssert (!top || [top isKindOfClass:[UILabel class]], @"WTF");
- NSAssert ( [left isKindOfClass:[UILabel class]], @"WTF");
- NSAssert ( ![mid isKindOfClass:[UILabel class]], @"WTF");
- NSAssert ( [right isKindOfClass:[UILabel class]], @"WTF");
-
- // 3 elements: control at top of cell.
- // 4 elements: center the control vertically.
- 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
- r.origin.x = LEFT_MARGIN;
-# else
- r.origin.x = left_width;
-# endif
- r.origin.y = (hh - r.size.height) / 2;
- r.size.width = right_edge - r.origin.x;
- [mid setFrame:r];
-
- 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 (ww - LEFT_MARGIN*2, 100000)
- lineBreakMode:[top lineBreakMode]];
-# 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.
- 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.
- 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.
- CGRect r = [ctl frame];
- r.origin.x = LEFT_MARGIN;
- r.origin.y = 0;
- r.size.width = right_edge - r.origin.x;
- r.size.height = hh;
- [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;
-}
-# endif // USE_IPHONE
-
-
-/* When this object is instantiated, it parses the XML file and creates
- controls on itself that are hooked up to the appropriate preferences.
- The default size of the view is just big enough to hold them all.
- */
-- (id)initWithXML: (NSData *) xml_data
- options: (const XrmOptionDescRec *) _opts
- controller: (NSUserDefaultsController *) _prefs
- globalController: (NSUserDefaultsController *) _globalPrefs
- defaults: (NSDictionary *) _defs
-{
-# ifndef USE_IPHONE
- self = [super init];
-# else // !USE_IPHONE
- self = [super initWithStyle:UITableViewStyleGrouped];
- self.title = [saver_name stringByAppendingString:@" Settings"];
-# endif // !USE_IPHONE
- if (! self) return 0;
-
- // instance variables
- opts = _opts;
- defaultOptions = _defs;
- userDefaultsController = [_prefs retain];
- globalDefaultsController = [_globalPrefs retain];
-
- NSXMLParser *xmlDoc = [[NSXMLParser alloc] initWithData:xml_data];
-
- if (!xmlDoc) {
- 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: %@: %@",
- [[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;
-
-# ifdef USE_IPHONE
- [self addResetButton];
-# endif
-
- return self;
-}
-
-
-- (void) dealloc
-{
- [saver_name release];
- [userDefaultsController release];
- [globalDefaultsController release];
-# ifdef USE_IPHONE
- [controls release];
- [pref_keys release];
- [pref_ctls release];
-# ifdef USE_PICKER_VIEW
- [picker_values release];
-# endif
-# endif
- [super dealloc];
-}
-
-@end