2013-10-21 23:17:51 -03: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 20:25:58 -03:00
NSAlert * alert = [ NSAlert alertWithMessageText : SULocalizedString ( @ "Update Error!" , nil ) defaultButton : SULocalizedString ( @ "Cancel Update" , nil ) alternateButton : nil otherButton : nil informativeTextWithFormat : @ "%@" , [ error localizedDescription ] ] ;
2013-10-21 23:17:51 -03: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