X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=OSX%2FXScreenSaverConfigSheet.m;h=ebf33827ea9747ba397588fbe336cfe45a1bc529;hb=4ade52359b6eba3621566dac79793a33aa4c915f;hp=e179dd0b0f8b4160ba63397e0c1faa5c6f39dbaf;hpb=c70f94f648d51bb4828193124f325fa52b0e57f3;p=xscreensaver diff --git a/OSX/XScreenSaverConfigSheet.m b/OSX/XScreenSaverConfigSheet.m index e179dd0b..ebf33827 100644 --- a/OSX/XScreenSaverConfigSheet.m +++ b/OSX/XScreenSaverConfigSheet.m @@ -1,4 +1,4 @@ -/* xscreensaver, Copyright (c) 2006-2012 Jamie Zawinski +/* xscreensaver, Copyright (c) 2006-2013 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -48,6 +48,7 @@ #endif // USE_IPHONE #undef LABEL_ABOVE_SLIDER +#define USE_HTML_LABELS #pragma mark XML Parser @@ -191,6 +192,222 @@ typedef enum { SimpleXMLCommentKind, # endif // !USE_PICKER_VIEW +# pragma mark Implementing labels with clickable links + +#if defined(USE_IPHONE) && defined(USE_HTML_LABELS) + +@interface HTMLLabel : UIView +{ + 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 = (UIViewAutoresizingFlexibleWidth | + UIViewAutoresizingFlexibleHeight); + webView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | + UIViewAutoresizingFlexibleHeight); + [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: + @"" + "" + "" +// "" + "" + "" + "" + "%@" + "" + "", + [font fontName], + [font pointSize], + [font lineHeight], + h]; + [webView loadHTMLString:h2 baseURL:[NSURL URLWithString:@""]]; +} + + +static char *anchorize (const char *url); + +- (void) setText: (NSString *)t +{ + t = [t stringByReplacingOccurrencesOfString:@"&" withString:@"&"]; + t = [t stringByReplacingOccurrencesOfString:@"<" withString:@"<"]; + t = [t stringByReplacingOccurrencesOfString:@">" withString:@">"]; + t = [t stringByReplacingOccurrencesOfString:@"\n\n" withString:@"

"]; + t = [t stringByReplacingOccurrencesOfString:@"

" + withString:@"

        "]; + t = [t stringByReplacingOccurrencesOfString:@"\n " + withString:@"
        "]; + + 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: @"%@
", s, a2]; + free (anchor); + } + h = [NSString stringWithFormat: @"%@ %@", h, s]; + } + [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]; + [self setHTML: html]; + [webView reload]; +} + + +- (NSString *) stripTags:(NSString *)str +{ + NSString *result = @""; + + str = [str stringByReplacingOccurrencesOfString:@"

" + withString:@"

" + options:NSCaseInsensitiveSearch + range:NSMakeRange(0, [str length])]; + str = [str stringByReplacingOccurrencesOfString:@"
" + withString:@"\n" + options:NSCaseInsensitiveSearch + range:NSMakeRange(0, [str length])]; + + 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]; + } + 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 I can't make that work. + Maybe because it loads async? + */ +# 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 sizeWithFont: font + constrainedToSize: s + lineBreakMode:NSLineBreakByWordWrapping]; + + // GAAAH. Add one more line, or the UIWebView is still scrollable! + // The text is sized right, but it lets you scroll it up anyway. + s.height += [font pointSize]; + + 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) @@ -316,7 +533,12 @@ static void layout_group (NSView *group, BOOL horiz_p); if ([active_text_field canResignFirstResponder]) [active_text_field resignFirstResponder]; NSString *pref_key = [pref_keys objectAtIndex: [sender tag]]; - double v = [sender value]; + + // Hacky API. See comment in InvertedSlider.m. + double v = ([sender isKindOfClass: [InvertedSlider class]] + ? [(InvertedSlider *) sender transformedValue] + : [sender value]); + if (v == (int) v) [userDefaultsController setInteger:v forKey:pref_key]; else @@ -472,7 +694,11 @@ static void layout_group (NSView *group, BOOL horiz_p); if ([control isKindOfClass:[UISlider class]]) { sel = @selector(sliderAction:); - [(UISlider *) control setValue: dval]; + // 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)]; @@ -682,6 +908,8 @@ anchorize (const char *url) } +#if !defined(USE_IPHONE) || !defined(USE_HTML_LABELS) + /* Converts any http: URLs in the given text field to clickable links. */ static void @@ -766,6 +994,9 @@ hreffify (NSText *nstext) # endif } +#endif /* !USE_IPHONE || !USE_HTML_LABELS */ + + #pragma mark Creating controls from XML @@ -813,6 +1044,26 @@ hreffify (NSText *nstext) 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 } @@ -858,7 +1109,7 @@ hreffify (NSText *nstext) [lab setBackgroundColor:[UIColor clearColor]]; [lab setNumberOfLines:0]; // unlimited // [lab setLineBreakMode:UILineBreakModeWordWrap]; - [lab setLineBreakMode:UILineBreakModeHeadTruncation]; + [lab setLineBreakMode:NSLineBreakByTruncatingHead]; [lab setAutoresizingMask: (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)]; # endif // USE_IPHONE @@ -1061,7 +1312,7 @@ hreffify (NSText *nstext) [lab setFrame:rect]; [self placeChild:lab on:parent]; # else // USE_IPHONE - [lab setTextAlignment: UITextAlignmentRight]; + [lab setTextAlignment: NSTextAlignmentRight]; [self placeChild:lab on:parent right:(label ? YES : NO)]; # endif // USE_IPHONE @@ -1431,7 +1682,7 @@ set_menu_item_object (NSMenuItem *item, NSObject *obj) for (NSArray *item in items) { RadioButton *b = [[RadioButton alloc] initWithIndex:i items:items]; - [b setLineBreakMode:UILineBreakModeHeadTruncation]; + [b setLineBreakMode:NSLineBreakByTruncatingHead]; [b setFont:[NSFont boldSystemFontOfSize: FONT_SIZE]]; [self placeChild:b on:parent]; i++; @@ -1479,25 +1730,25 @@ set_menu_item_object (NSMenuItem *item, NSObject *obj) hreffify (lab); boldify (lab); [lab sizeToFit]; -# else // USE_IPHONE - /* There's no way to put rich text or links inside a UILabel. - - I guess Apple expects us to use a UIWebView for this -- but there's - no way to measure how tall the HTML-rendered text is (the answer is - not: "document.height" via JavaScript) so we can't put the - properly-sized cell in the table. - - This is some serious bullshit. +# else // USE_IPHONE - Another option would be to subclass UILabel and replace its drawRect - with new code that uses CTLineDraw. But that's a huge hassle. - */ +# 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 setFrame:rect]; + [lab sizeToFit]; +# endif // USE_HTML_LABELS + [self placeSeparator]; + # endif // USE_IPHONE [self placeChild:lab on:parent]; @@ -1553,7 +1804,7 @@ set_menu_item_object (NSMenuItem *item, NSObject *obj) txt.font = [UIFont systemFontOfSize: FONT_SIZE]; txt.placeholder = @""; txt.borderStyle = UITextBorderStyleRoundedRect; - txt.textAlignment = UITextAlignmentRight; + txt.textAlignment = NSTextAlignmentRight; txt.keyboardType = UIKeyboardTypeDefault; // Full kbd txt.autocorrectionType = UITextAutocorrectionTypeNo; txt.autocapitalizationType = UITextAutocapitalizationTypeNone; @@ -2024,7 +2275,6 @@ find_text_field_of_button (NSButton *button) - (void) makeImageLoaderControlBox:(NSXMLNode *)node on:(NSView *)parent { -# ifndef USE_IPHONE /* [x] Grab desktop images [ ] Choose random image: @@ -2039,11 +2289,19 @@ find_text_field_of_button (NSButton *button) 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: [NSDictionary dictionaryWithObjectsAndKeys: @"grabDesktopImages", @"id", - @"Grab desktop images", @"_label", + @ SCREENS, @"_label", @"-no-grab-desktop", @"arg-unset", nil]]; [self makeCheckbox:node2 on:parent]; @@ -2052,7 +2310,7 @@ find_text_field_of_button (NSButton *button) [node2 setAttributesAsDictionary: [NSDictionary dictionaryWithObjectsAndKeys: @"chooseRandomImages", @"id", - @"Choose random images", @"_label", + @ PHOTOS, @"_label", @"-choose-random-images", @"arg-set", nil]]; [self makeCheckbox:node2 on:parent]; @@ -2067,6 +2325,10 @@ find_text_field_of_button (NSButton *button) [self makeFileSelector:node2 on:parent dirsOnly:YES withLabel:YES editable:YES]; +# undef SCREENS +# undef PHOTOS + +# ifndef USE_IPHONE // Add a second, explanatory label below the file/URL selector. LABEL *lab2 = 0; @@ -2079,7 +2341,7 @@ find_text_field_of_button (NSButton *button) r2.origin.y += 14; [lab2 setFrameOrigin:r2.origin]; [lab2 release]; -# endif // !USE_IPHONE +# endif // USE_IPHONE } @@ -2612,6 +2874,8 @@ wrap_with_buttons (NSWindow *window, NSView *panel) } saver_name = [self parseXScreenSaverTag: node]; + saver_name = [saver_name stringByReplacingOccurrencesOfString:@" " + withString:@""]; [saver_name retain]; # ifndef USE_IPHONE @@ -2831,7 +3095,20 @@ wrap_with_buttons (NSWindow *window, NSView *panel) [t sizeToFit]; r = t.frame; h = r.size.height + LINE_SPACING * 3; +# ifdef USE_HTML_LABELS + + } else if ([ctl isKindOfClass:[HTMLLabel class]]) { + + HTMLLabel *t = (HTMLLabel *) ctl; + CGRect r = t.frame; + r.size.width = [tv frame].size.width; + r.size.width -= LEFT_MARGIN * 2; + [t setFrame:r]; + [t sizeToFit]; + r = t.frame; + h = r.size.height + LINE_SPACING * 3; +# endif // USE_HTML_LABELS } else { CGFloat h2 = [ctl frame].size.height; h2 += LINE_SPACING * 2;