Permalink
Cannot retrieve contributors at this time
Fetching contributors…

// | |
// SUUpdateAlert.m | |
// Sparkle | |
// | |
// Created by Andy Matuschak on 3/12/06. | |
// Copyright 2006 Andy Matuschak. All rights reserved. | |
// | |
// ----------------------------------------------------------------------------- | |
// Headers: | |
// ----------------------------------------------------------------------------- | |
#import "SUUpdateAlert.h" | |
#import "SUHost.h" | |
#import <WebKit/WebKit.h> | |
#import "SUConstants.h" | |
@interface WebView (SUTenFiveProperty) | |
-(void) setDrawsBackground: (BOOL)state; | |
@end | |
@implementation SUUpdateAlert | |
- (id)initWithAppcastItem:(SUAppcastItem *)item host:(SUHost *)aHost | |
{ | |
self = [super initWithHost:host windowNibName:@"SUUpdateAlert"]; | |
if (self) | |
{ | |
host = [aHost retain]; | |
updateItem = [item retain]; | |
[self setShouldCascadeWindows:NO]; | |
// Alex: This dummy line makes sure that the binary is linked against WebKit. | |
// The SUUpdateAlert.xib file contains a WebView and if we don't link against WebKit, | |
// we will get a runtime crash when decoding the NIB. It is better to get a link error. | |
[WebView MIMETypesShownAsHTML]; | |
} | |
return self; | |
} | |
- (NSString *)description { return [NSString stringWithFormat:@"%@ <%@>", [self class], [host bundlePath]]; } | |
- (void)dealloc | |
{ | |
[updateItem release]; | |
[host release]; | |
[super dealloc]; | |
} | |
- (void)setVersionDisplayer: (id<SUVersionDisplay>)disp | |
{ | |
versionDisplayer = disp; | |
} | |
- (void)endWithSelection:(SUUpdateAlertChoice)choice | |
{ | |
[releaseNotesView stopLoading:self]; | |
[releaseNotesView setFrameLoadDelegate:nil]; | |
[releaseNotesView setPolicyDelegate:nil]; | |
[releaseNotesView removeFromSuperview]; // Otherwise it gets sent Esc presses (why?!) and gets very confused. | |
[self close]; | |
if ([delegate respondsToSelector:@selector(updateAlert:finishedWithChoice:)]) | |
[delegate updateAlert:self finishedWithChoice:choice]; | |
} | |
- (IBAction)installUpdate: (id)sender | |
{ | |
[self endWithSelection:SUInstallUpdateChoice]; | |
} | |
- (IBAction)openInfoURL: (id)sender | |
{ | |
[self endWithSelection:SUOpenInfoURLChoice]; | |
} | |
- (IBAction)skipThisVersion: (id)sender | |
{ | |
[self endWithSelection:SUSkipThisVersionChoice]; | |
} | |
- (IBAction)remindMeLater: (id)sender | |
{ | |
[self endWithSelection:SURemindMeLaterChoice]; | |
} | |
- (void)displayReleaseNotes | |
{ | |
// Set the default font | |
[releaseNotesView setPreferencesIdentifier:[SPARKLE_BUNDLE bundleIdentifier]]; | |
[[releaseNotesView preferences] setStandardFontFamily:[[NSFont systemFontOfSize:8] familyName]]; | |
[[releaseNotesView preferences] setDefaultFontSize:(int)[NSFont systemFontSizeForControlSize:NSSmallControlSize]]; | |
[releaseNotesView setFrameLoadDelegate:self]; | |
[releaseNotesView setPolicyDelegate:self]; | |
// Stick a nice big spinner in the middle of the web view until the page is loaded. | |
NSRect frame = [[releaseNotesView superview] frame]; | |
releaseNotesSpinner = [[[NSProgressIndicator alloc] initWithFrame:NSMakeRect(NSMidX(frame)-16, NSMidY(frame)-16, 32, 32)] autorelease]; | |
[releaseNotesSpinner setStyle:NSProgressIndicatorSpinningStyle]; | |
[releaseNotesSpinner startAnimation:self]; | |
webViewFinishedLoading = NO; | |
[[releaseNotesView superview] addSubview:releaseNotesSpinner]; | |
// If there's a release notes URL, load it; otherwise, just stick the contents of the description into the web view. | |
if ([updateItem releaseNotesURL]) | |
{ | |
if ([[updateItem releaseNotesURL] isFileURL]) | |
{ | |
[[releaseNotesView mainFrame] loadHTMLString:@"Release notes with file:// URLs are not supported for security reasons—Javascript would be able to read files on your file system." baseURL:nil]; | |
} | |
else | |
{ | |
[[releaseNotesView mainFrame] loadRequest:[NSURLRequest requestWithURL:[updateItem releaseNotesURL] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:30]]; | |
} | |
} | |
else | |
{ | |
[[releaseNotesView mainFrame] loadHTMLString:[updateItem itemDescription] baseURL:nil]; | |
} | |
} | |
- (BOOL)showsReleaseNotes | |
{ | |
NSNumber *shouldShowReleaseNotes = [host objectForInfoDictionaryKey:SUShowReleaseNotesKey]; | |
if (shouldShowReleaseNotes == nil) | |
{ | |
// UK 2007-09-18: Don't show release notes if RSS item contains no description and no release notes URL: | |
return( ([updateItem itemDescription] != nil | |
&& [[[updateItem itemDescription] stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]] length] > 0) | |
|| [updateItem releaseNotesURL] != nil ); | |
} | |
else | |
return [shouldShowReleaseNotes boolValue]; | |
} | |
- (BOOL)allowsAutomaticUpdates | |
{ | |
BOOL allowAutoUpdates = YES; // Defaults to YES. | |
if( [host objectForInfoDictionaryKey:SUAllowsAutomaticUpdatesKey] ) | |
allowAutoUpdates = [host boolForInfoDictionaryKey: SUAllowsAutomaticUpdatesKey]; | |
// UK 2007-08-31: Give delegate a chance to modify this choice: | |
if( delegate && [delegate respondsToSelector: @selector(updateAlert:shouldAllowAutoUpdate:)] ) | |
[delegate updateAlert: self shouldAllowAutoUpdate: &allowAutoUpdates]; | |
return allowAutoUpdates; | |
} | |
- (void)awakeFromNib | |
{ | |
NSString* sizeStr = [host objectForInfoDictionaryKey:SUFixedHTMLDisplaySizeKey]; | |
if( [host isBackgroundApplication] ) | |
[[self window] setLevel:NSFloatingWindowLevel]; // This means the window will float over all other apps, if our app is switched out ?! UK 2007-09-04 | |
[[self window] setFrameAutosaveName: sizeStr ? @"" : @"SUUpdateAlertFrame"]; | |
// We're gonna do some frame magic to match the window's size to the description field and the presence of the release notes view. | |
NSRect frame = [[self window] frame]; | |
BOOL showReleaseNotes = [self showsReleaseNotes]; // UK 2007-09-18 | |
if (!showReleaseNotes) // UK 2007-09-18 | |
{ | |
// Resize the window to be appropriate for not having a huge release notes view. | |
frame.size.height -= [releaseNotesView frame].size.height + 40; // Extra 40 is for the release notes label and margin. | |
if ([self allowsAutomaticUpdates]) | |
frame.size.height += 10; // Make room for the check box. | |
// Hiding the resize handles is not enough on 10.5, you can still click | |
// where they would be, so we set the min/max sizes to be equal to | |
// inhibit resizing completely: | |
[[self window] setShowsResizeIndicator: NO]; | |
[[self window] setMinSize: frame.size]; | |
[[self window] setMaxSize: frame.size]; | |
} | |
else if (![self allowsAutomaticUpdates]) | |
{ | |
NSRect boxFrame = [[[releaseNotesView superview] superview] frame]; | |
boxFrame.origin.y -= 20; | |
boxFrame.size.height += 20; | |
[[[releaseNotesView superview] superview] setFrame:boxFrame]; | |
} | |
if( [updateItem fileURL] == nil ) // UK 2007-08-31 (whole if clause) | |
{ | |
[installButton setTitle: SULocalizedString( @"Learn More...", @"Alternate title for 'Install Update' button when there's no download in RSS feed." )]; | |
[installButton setAction: @selector(openInfoURL:)]; | |
} | |
// Make sure button widths are OK: | |
#define DISTANCE_BETWEEN_BUTTONS 3 | |
#define DISTANCE_BETWEEN_BUTTON_GROUPS 12 | |
CGFloat minimumWindowWidth = [[self window] frame].size.width -NSMaxX([installButton frame]) +NSMinX([skipButton frame]); // Distance between contents and left/right edge. | |
NSDictionary* attrs = [NSDictionary dictionaryWithObjectsAndKeys: [installButton font], NSFontAttributeName, nil]; | |
NSSize titleSize = [[installButton title] sizeWithAttributes: attrs]; | |
titleSize.width += (16 + 8) * 2; // 16 px for the end caps plus 8 px padding at each end or it'll look as ugly as calling -sizeToFit. | |
NSRect installBtnBox = [installButton frame]; | |
installBtnBox.origin.x += installBtnBox.size.width -titleSize.width; | |
installBtnBox.size.width = titleSize.width; | |
[installButton setFrame: installBtnBox]; | |
minimumWindowWidth += titleSize.width; | |
titleSize = [[laterButton title] sizeWithAttributes: attrs]; | |
titleSize.width += (16 + 8) * 2; // 16 px for the end caps plus 8 px padding at each end or it'll look as ugly as calling -sizeToFit. | |
NSRect laterBtnBox = [installButton frame]; | |
laterBtnBox.origin.x = installBtnBox.origin.x -DISTANCE_BETWEEN_BUTTONS -titleSize.width; | |
laterBtnBox.size.width = titleSize.width; | |
[laterButton setFrame: laterBtnBox]; | |
minimumWindowWidth += DISTANCE_BETWEEN_BUTTONS +titleSize.width; | |
titleSize = [[skipButton title] sizeWithAttributes: attrs]; | |
titleSize.width += (16 + 8) * 2; // 16 px for the end caps plus 8 px padding at each end or it'll look as ugly as calling -sizeToFit. | |
NSRect skipBtnBox = [skipButton frame]; | |
skipBtnBox.size.width = titleSize.width; | |
[skipButton setFrame: skipBtnBox]; | |
minimumWindowWidth += DISTANCE_BETWEEN_BUTTON_GROUPS +titleSize.width; | |
if( showReleaseNotes ) // UK 2007-09-18 (whole block) | |
{ | |
if( sizeStr ) | |
{ | |
NSSize desiredSize = NSSizeFromString( sizeStr ); | |
NSSize sizeDiff = NSZeroSize; | |
// NSBox* boxView = (NSBox*)[[releaseNotesView superview] superview]; | |
//[boxView setBorderType: NSNoBorder]; | |
[releaseNotesView setDrawsBackground: NO]; | |
sizeDiff.width = desiredSize.width -[releaseNotesView frame].size.width; | |
sizeDiff.height = desiredSize.height -[releaseNotesView frame].size.height; | |
frame.size.width += sizeDiff.width; | |
frame.size.height += sizeDiff.height; | |
// No resizing: | |
[[self window] setShowsResizeIndicator:NO]; | |
[[self window] setMinSize:frame.size]; | |
[[self window] setMaxSize:frame.size]; | |
} | |
} | |
if( frame.size.width < minimumWindowWidth ) | |
frame.size.width = minimumWindowWidth; | |
[[self window] setFrame: frame display: NO]; | |
[[self window] center]; | |
if (showReleaseNotes) // UK 2007-09-18 | |
{ | |
[self displayReleaseNotes]; | |
} | |
[[[releaseNotesView superview] superview] setHidden: !showReleaseNotes]; // UK 2007-09-18 | |
} | |
-(BOOL)showsReleaseNotesText | |
{ | |
return( [host objectForInfoDictionaryKey:SUFixedHTMLDisplaySizeKey] == nil ); | |
} | |
- (BOOL)windowShouldClose:note | |
{ | |
[self endWithSelection:SURemindMeLaterChoice]; | |
return YES; | |
} | |
- (NSImage *)applicationIcon | |
{ | |
return [host icon]; | |
} | |
- (NSString *)titleText | |
{ | |
return [NSString stringWithFormat:SULocalizedString(@"A new version of %@ is available!", nil), [host name]]; | |
} | |
- (NSString *)descriptionText | |
{ | |
NSString *updateItemVersion = [updateItem displayVersionString]; | |
NSString *hostVersion = [host displayVersion]; | |
// Display more info if the version strings are the same; useful for betas. | |
if( !versionDisplayer && [updateItemVersion isEqualToString:hostVersion] ) | |
{ | |
updateItemVersion = [updateItemVersion stringByAppendingFormat:@" (%@)", [updateItem versionString]]; | |
hostVersion = [hostVersion stringByAppendingFormat:@" (%@)", [host version]]; | |
} | |
else | |
[versionDisplayer formatVersion: &updateItemVersion andVersion: &hostVersion]; | |
return [NSString stringWithFormat:SULocalizedString(@"%@ %@ is now available--you have %@. Would you like to download it now?", nil), [host name], updateItemVersion, hostVersion]; | |
} | |
- (void)webView:(WebView *)sender didFinishLoadForFrame:frame | |
{ | |
if ([frame parentFrame] == nil) { | |
webViewFinishedLoading = YES; | |
[releaseNotesSpinner setHidden:YES]; | |
[sender display]; // necessary to prevent weird scroll bar artifacting | |
} | |
} | |
- (void)webView:sender decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:frame decisionListener:listener | |
{ | |
if (webViewFinishedLoading) { | |
[[NSWorkspace sharedWorkspace] openURL:[request URL]]; | |
[listener ignore]; | |
} | |
else { | |
[listener use]; | |
} | |
} | |
// Clean up the contextual menu. | |
- (NSArray *)webView:(WebView *)sender contextMenuItemsForElement:(NSDictionary *)element defaultMenuItems:(NSArray *)defaultMenuItems | |
{ | |
NSMutableArray *webViewMenuItems = [[defaultMenuItems mutableCopy] autorelease]; | |
if (webViewMenuItems) | |
{ | |
NSEnumerator *itemEnumerator = [defaultMenuItems objectEnumerator]; | |
NSMenuItem *menuItem = nil; | |
while ((menuItem = [itemEnumerator nextObject])) | |
{ | |
NSInteger tag = [menuItem tag]; | |
switch (tag) | |
{ | |
case WebMenuItemTagOpenLinkInNewWindow: | |
case WebMenuItemTagDownloadLinkToDisk: | |
case WebMenuItemTagOpenImageInNewWindow: | |
case WebMenuItemTagDownloadImageToDisk: | |
case WebMenuItemTagOpenFrameInNewWindow: | |
case WebMenuItemTagGoBack: | |
case WebMenuItemTagGoForward: | |
case WebMenuItemTagStop: | |
case WebMenuItemTagReload: | |
[webViewMenuItems removeObjectIdenticalTo: menuItem]; | |
} | |
} | |
} | |
return webViewMenuItems; | |
} | |
- (void)setDelegate:del | |
{ | |
delegate = del; | |
} | |
@end |