2013-10-22 02:17:51 +00:00
/ /
/ / SUUIBasedUpdateDriver . m
/ / Sparkle
/ /
/ / Created by Andy Matuschak on 5 / 5 / 08.
/ / Copyright 2008 Andy Matuschak . All rights reserved .
/ /
#import "SUUIBasedUpdateDriver . h "
#import "SUUpdateAlert . h "
#import "SUUpdater_Private . h "
#import "SUHost . h "
#import "SUStatusController . h "
#import "SUConstants . h "
#import "SUPasswordPrompt . h "
@ implementation SUUIBasedUpdateDriver
- ( void ) didFindValidUpdate
{
updateAlert = [ [ SUUpdateAlert alloc ] initWithAppcastItem : updateItem host : host ] ;
[ updateAlert setDelegate : self ] ;
id < SUVersionDisplay > versDisp = nil ;
if ( [ [ updater delegate ] respondsToSelector : @ selector ( versionDisplayerForUpdater : ) ] )
versDisp = [ [ updater delegate ] versionDisplayerForUpdater : updater ] ;
[ updateAlert setVersionDisplayer : versDisp ] ;
if ( [ [ updater delegate ] respondsToSelector : @ selector ( updater : didFindValidUpdate : ) ] )
[ [ updater delegate ] updater : updater didFindValidUpdate : updateItem ] ;
/ / If the app is a menubar app or the like , we need to focus it first and alter the
/ / update prompt to behave like a normal window . Otherwise if the window were hidden
/ / there may be no way for the application to be activated to make it visible again .
if ( [ host isBackgroundApplication ] )
{
[ [ updateAlert window ] setHidesOnDeactivate : NO ] ;
[ NSApp activateIgnoringOtherApps : YES ] ;
}
/ / Only show the update alert if the app is active ; otherwise , we ' ll wait until it is .
if ( [ NSApp isActive ] )
[ [ updateAlert window ] makeKeyAndOrderFront : self ] ;
else
[ [ NSNotificationCenter defaultCenter ] addObserver : self selector : @ selector ( applicationDidBecomeActive : ) name : NSApplicationDidBecomeActiveNotification object : NSApp ] ;
}
- ( void ) didNotFindUpdate
{
if ( [ [ updater delegate ] respondsToSelector : @ selector ( updaterDidNotFindUpdate : ) ] )
[ [ updater delegate ] updaterDidNotFindUpdate : updater ] ;
NSAlert * alert = [ NSAlert alertWithMessageText : SULocalizedString ( @ "You ' re up - to - date !", nil ) defaultButton : SULocalizedString ( @ "OK ", nil ) alternateButton : nil otherButton : nil informativeTextWithFormat : SULocalizedString ( @ "%@ %@ is currently the newest version available.", nil), [host name], [host displayVersion]];
[ self showModalAlert : alert ] ;
[ self abortUpdate ] ;
}
- ( void ) applicationDidBecomeActive : ( NSNotification * ) aNotification
{
[ [ updateAlert window ] makeKeyAndOrderFront : self ] ;
[ [ NSNotificationCenter defaultCenter ] removeObserver : self name : @ "NSApplicationDidBecomeActiveNotification " object : NSApp ] ;
}
- ( void ) updateAlert : ( SUUpdateAlert * ) alert finishedWithChoice : ( SUUpdateAlertChoice ) choice
{
[ updateAlert release ] ; updateAlert = nil ;
[ host setObject : nil forUserDefaultsKey : SUSkippedVersionKey ] ;
switch ( choice )
{
case SUInstallUpdateChoice :
statusController = [ [ SUStatusController alloc ] initWithHost : host ] ;
[ statusController beginActionWithTitle : SULocalizedString ( @ "Downloading update ...", @"Take care not to overflow the status window.") maxProgressValue:0.0 statusText:nil];
[ statusController setButtonTitle : SULocalizedString ( @ "Cancel ", nil ) target : self action : @ selector ( cancelDownload : ) isDefault : NO ] ;
[ statusController showWindow : self ] ;
[ self downloadUpdate ] ;
break ;
case SUOpenInfoURLChoice :
[ [ NSWorkspace sharedWorkspace ] openURL : [ updateItem infoURL ] ] ;
[ self abortUpdate ] ;
break ;
case SUSkipThisVersionChoice :
[ host setObject : [ updateItem versionString ] forUserDefaultsKey : SUSkippedVersionKey ] ;
[ self abortUpdate ] ;
break ;
case SURemindMeLaterChoice :
[ self abortUpdate ] ;
break ;
}
}
- ( void ) download : ( NSURLDownload * ) download didReceiveResponse : ( NSURLResponse * ) response
{
[ statusController setMaxProgressValue : [ response expectedContentLength ] ] ;
}
- ( NSString * ) humanReadableSizeFromDouble : ( double ) value
{
if ( value < 1000 )
return [ NSString stringWithFormat : @ "%.0lf %@", value, SULocalizedString(@"B", @"the unit for bytes")];
if ( value < 1000 * 1000 )
return [ NSString stringWithFormat : @ "%.0lf %@", value / 1000.0, SULocalizedString(@"KB", @"the unit for kilobytes")];
if ( value < 1000 * 1000 * 1000 )
return [ NSString stringWithFormat : @ "%.1lf %@", value / 1000.0 / 1000.0, SULocalizedString(@"MB", @"the unit for megabytes")];
return [ NSString stringWithFormat : @ "%.2lf %@", value / 1000.0 / 1000.0 / 1000.0, SULocalizedString(@"GB", @"the unit for gigabytes")];
}
- ( void ) download : ( NSURLDownload * ) download didReceiveDataOfLength : ( NSUInteger ) length
{
[ statusController setProgressValue : [ statusController progressValue ] + ( double ) length ] ;
if ( [ statusController maxProgressValue ] > 0.0 )
[ statusController setStatusText : [ NSString stringWithFormat : SULocalizedString ( @ "%@ of %@", nil), [self humanReadableSizeFromDouble:[statusController progressValue]], [self humanReadableSizeFromDouble:[statusController maxProgressValue]]]];
else
[ statusController setStatusText : [ NSString stringWithFormat : SULocalizedString ( @ "%@ downloaded", nil), [self humanReadableSizeFromDouble:[statusController progressValue]]]];
}
- ( IBAction ) cancelDownload : ( id ) sender
{
if ( download )
[ download cancel ] ;
[ self abortUpdate ] ;
}
- ( void ) extractUpdate
{
/ / Now we have to extract the downloaded archive .
[ statusController beginActionWithTitle : SULocalizedString ( @ "Extracting update ...", @"Take care not to overflow the status window.") maxProgressValue:0.0 statusText:nil];
[ statusController setButtonEnabled : NO ] ;
[ super extractUpdate ] ;
}
- ( void ) unarchiver : ( SUUnarchiver * ) ua extractedLength : ( unsigned long ) length
{
/ / We do this here instead of in extractUpdate so that we only have a determinate progress bar for archives with progress .
if ( [ statusController maxProgressValue ] == 0.0 )
{
NSDictionary * attributes ;
#if MAC_OS_X_VERSION_MIN_REQUIRED < = MAC_OS_X_VERSION_10_4
attributes = [ [ NSFileManager defaultManager ] fileAttributesAtPath : downloadPath traverseLink : NO ] ;
#else
attributes = [ [ NSFileManager defaultManager ] attributesOfItemAtPath : downloadPath error : nil ] ;
#endif
[ statusController setMaxProgressValue : [ [ attributes objectForKey : NSFileSize ] doubleValue ] ] ;
}
[ statusController setProgressValue : [ statusController progressValue ] + ( double ) length ] ;
}
- ( void ) unarchiverDidFinish : ( SUUnarchiver * ) ua
{
[ statusController beginActionWithTitle : SULocalizedString ( @ "Ready to Install ", nil ) maxProgressValue : 1.0 statusText : nil ] ;
[ statusController setProgressValue : 1.0 ] ; / / Fill the bar .
[ statusController setButtonEnabled : YES ] ;
[ statusController setButtonTitle : SULocalizedString ( @ "Install and Relaunch ", nil ) target : self action : @ selector ( installAndRestart : ) isDefault : YES ] ;
[ [ statusController window ] makeKeyAndOrderFront : self ] ;
[ NSApp requestUserAttention : NSInformationalRequest ] ;
}
- ( void ) unarchiver : ( SUUnarchiver * ) unarchiver requiresPasswordReturnedViaInvocation : ( NSInvocation * ) invocation
{
SUPasswordPrompt * prompt = [ [ SUPasswordPrompt alloc ] initWithHost : host ] ;
NSString * password = nil ;
if ( [ prompt run ] )
{
password = [ prompt password ] ;
}
[ prompt release ] ;
[ invocation setArgument : & password atIndex : 2 ] ;
[ invocation invoke ] ;
}
- ( void ) installAndRestart : ( id ) sender
{
[ self installWithToolAndRelaunch : YES ] ;
}
- ( void ) installWithToolAndRelaunch : ( BOOL ) relaunch
{
[ statusController beginActionWithTitle : SULocalizedString ( @ "Installing update ...", @"Take care not to overflow the status window.") maxProgressValue:0.0 statusText:nil];
[ statusController setButtonEnabled : NO ] ;
[ super installWithToolAndRelaunch : relaunch ] ;
/ / if a user chooses to NOT relaunch the app ( as is the case with WebKit
/ / when it asks you if you are sure you want to close the app with multiple
/ / tabs open ) , the status window still stays on the screen and obscures
/ / other windows ; with this fix , it doesn ' t
if ( statusController )
{
[ statusController close ] ;
[ statusController autorelease ] ;
statusController = nil ;
}
}
- ( void ) abortUpdateWithError : ( NSError * ) error
{
2013-10-23 23:25:58 +00:00
NSAlert * alert = [ NSAlert alertWithMessageText : SULocalizedString ( @ "Update Error !", nil ) defaultButton : SULocalizedString ( @ "Cancel Update ", nil ) alternateButton : nil otherButton : nil informativeTextWithFormat : @ "%@", [error localizedDescription]];
2013-10-22 02:17:51 +00:00
[ self showModalAlert : alert ] ;
[ super abortUpdateWithError : error ] ;
}
- ( void ) abortUpdate
{
if ( statusController )
{
[ statusController close ] ;
[ statusController autorelease ] ;
statusController = nil ;
}
[ super abortUpdate ] ;
}
- ( void ) showModalAlert : ( NSAlert * ) alert
{
if ( [ [ updater delegate ] respondsToSelector : @ selector ( updaterWillShowModalAlert : ) ] )
[ [ updater delegate ] updaterWillShowModalAlert : updater ] ;
/ / When showing a modal alert we need to ensure that background applications
/ / are focused to inform the user since there is no dock icon to notify them .
if ( [ host isBackgroundApplication ] ) { [ NSApp activateIgnoringOtherApps : YES ] ; }
[ alert setIcon : [ host icon ] ] ;
[ alert runModal ] ;
if ( [ [ updater delegate ] respondsToSelector : @ selector ( updaterDidShowModalAlert : ) ] )
[ [ updater delegate ] updaterDidShowModalAlert : updater ] ;
}
@ end