mirror of
https://github.com/zhigang1992/RubyMotion.git
synced 2026-05-02 06:24:57 +08:00
371 lines
13 KiB
Objective-C
371 lines
13 KiB
Objective-C
//
|
|
// RMMainWindowController.m
|
|
// RubyMotion Installer
|
|
//
|
|
// Created by lrz on 3/31/12.
|
|
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
|
|
//
|
|
|
|
#import "RMMainWindowController.h"
|
|
|
|
@implementation RMMainWindowController
|
|
|
|
+ (id)open
|
|
{
|
|
RMMainWindowController *controller = [[RMMainWindowController alloc] initWithWindowNibName:@"MainWindow"];
|
|
[controller window];
|
|
return controller;
|
|
}
|
|
|
|
- (void)windowDidLoad
|
|
{
|
|
[super windowDidLoad];
|
|
currentStep = 1;
|
|
softwareInstalled = false;
|
|
}
|
|
|
|
- (NSView *)viewForStep:(int)step
|
|
{
|
|
switch (step) {
|
|
case 1:
|
|
return view1;
|
|
case 2:
|
|
return view2;
|
|
case 3:
|
|
return view3;
|
|
case 4:
|
|
return view4;
|
|
case 5:
|
|
return view5;
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
- (void)contactSupport:(id)sender
|
|
{
|
|
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"mailto:info@hipbyte.com"]];
|
|
}
|
|
|
|
- (void)quitStep:(id)sender
|
|
{
|
|
if (currentStep == 5) {
|
|
if ([openGettingStartedGuideButton state] == NSOnState) {
|
|
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://www.rubymotion.com/developer-center/guides/getting-started"]];
|
|
}
|
|
}
|
|
[[NSApplication sharedApplication] terminate:sender];
|
|
}
|
|
|
|
static int numberOfShakes = 3;
|
|
static float durationOfShake = 0.5f;
|
|
static float vigourOfShake = 0.02f;
|
|
|
|
- (CAKeyframeAnimation *)shakeAnimation:(NSRect)frame
|
|
{
|
|
CAKeyframeAnimation *shakeAnimation = [CAKeyframeAnimation animation];
|
|
|
|
CGMutablePathRef shakePath = CGPathCreateMutable();
|
|
CGPathMoveToPoint(shakePath, NULL, NSMinX(frame), NSMinY(frame));
|
|
int index;
|
|
for (index = 0; index < numberOfShakes; ++index)
|
|
{
|
|
CGPathAddLineToPoint(shakePath, NULL, NSMinX(frame) - frame.size.width * vigourOfShake, NSMinY(frame));
|
|
CGPathAddLineToPoint(shakePath, NULL, NSMinX(frame) + frame.size.width * vigourOfShake, NSMinY(frame));
|
|
}
|
|
CGPathCloseSubpath(shakePath);
|
|
shakeAnimation.path = shakePath;
|
|
shakeAnimation.duration = durationOfShake;
|
|
return shakeAnimation;
|
|
}
|
|
|
|
- (void)shakeWindow
|
|
{
|
|
[[self window] setAnimations:[NSDictionary dictionaryWithObject:[self shakeAnimation:[[self window] frame]] forKey:@"frameOrigin"]];
|
|
[[[self window] animator] setFrameOrigin:[[self window] frame].origin];
|
|
}
|
|
|
|
- (void)licenseKeyInvalid
|
|
{
|
|
static int failures = 0;
|
|
failures++;
|
|
if (failures >= 3) {
|
|
[licenseKeyDescriptiveLabel setTextColor:[NSColor redColor]];
|
|
}
|
|
[self shakeWindow];
|
|
}
|
|
|
|
static int
|
|
sudoCommand(const char *myToolPath, char **myArguments)
|
|
{
|
|
OSStatus myStatus;
|
|
AuthorizationFlags myFlags = kAuthorizationFlagDefaults;
|
|
AuthorizationRef myAuthorizationRef = 0;
|
|
|
|
if (myAuthorizationRef == 0) {
|
|
myStatus = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, myFlags, &myAuthorizationRef);
|
|
if (myStatus != errAuthorizationSuccess) {
|
|
goto DoneWorking;
|
|
}
|
|
}
|
|
|
|
AuthorizationItem myItems = {kAuthorizationRightExecute, 0, NULL, 0};
|
|
AuthorizationRights myRights = {1, &myItems};
|
|
|
|
myFlags = kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed | kAuthorizationFlagPreAuthorize | kAuthorizationFlagExtendRights;
|
|
|
|
myStatus = AuthorizationCopyRights (myAuthorizationRef, &myRights, NULL, myFlags, NULL );
|
|
|
|
if (myStatus != errAuthorizationSuccess) {
|
|
goto DoneWorking;
|
|
}
|
|
|
|
FILE *myCommunicationsPipe = NULL;
|
|
char myReadBuffer[128];
|
|
|
|
myFlags = kAuthorizationFlagDefaults;
|
|
myStatus = AuthorizationExecuteWithPrivileges(myAuthorizationRef, myToolPath, myFlags, (char **)myArguments, &myCommunicationsPipe);
|
|
|
|
if (myStatus == errAuthorizationSuccess) {
|
|
while (true) {
|
|
int bytesRead = read (fileno (myCommunicationsPipe), myReadBuffer, sizeof (myReadBuffer));
|
|
if (bytesRead < 1) {
|
|
goto DoneWorking;
|
|
}
|
|
write (fileno (stdout), myReadBuffer, bytesRead);
|
|
}
|
|
}
|
|
|
|
DoneWorking:
|
|
if (myAuthorizationRef != 0) {
|
|
AuthorizationFree (myAuthorizationRef, kAuthorizationFlagDefaults);
|
|
}
|
|
return myStatus;
|
|
}
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
|
|
|
|
- (void)installPackage
|
|
{
|
|
assert(installerPath != nil);
|
|
|
|
[installProgressStatusLabel setStringValue:@"Installing files..."];
|
|
|
|
NSString *installerExecPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"installer.sh"];
|
|
|
|
[@"#!/bin/sh\n\n/usr/sbin/installer -pkg $1 -target / \n/bin/echo $2 > /Library/RubyMotion/license.key \n" writeToFile:installerExecPath atomically:YES encoding:NSUTF8StringEncoding error:nil];
|
|
chmod([installerExecPath fileSystemRepresentation], S_IXUSR | S_IXGRP | S_IXOTH);
|
|
|
|
char *install_args[] = { (char *)[installerPath fileSystemRepresentation], (char *)[licenseKey UTF8String], NULL };
|
|
int err = sudoCommand([installerExecPath fileSystemRepresentation], install_args);
|
|
if (err != 0) {
|
|
[[NSAlert alertWithMessageText:@"Installer Error" defaultButton:@"Quit" alternateButton:@"" otherButton:@"" informativeTextWithFormat:@"An error (%d) happened when installing the software on your system. Please launch the installer application again and contact support if the problem still persists.", err] runModal];
|
|
[[NSApplication sharedApplication] terminate:nil];
|
|
}
|
|
|
|
softwareInstalled = true;
|
|
[self nextStep:nil];
|
|
}
|
|
|
|
- (void)download:(NSURLDownload *)download didReceiveResponse:(NSURLResponse *)response
|
|
{
|
|
[installProgressStatusLabel setStringValue:@"Downloading product..."];
|
|
expectedDownloadLength = [response expectedContentLength];
|
|
}
|
|
|
|
- (void)download:(NSURLDownload *)download didReceiveDataOfLength:(NSUInteger)length
|
|
{
|
|
downloadDataReceived += length;
|
|
[installProgressIndicator setIndeterminate:NO];
|
|
[installProgressIndicator setDoubleValue:((100.0 / expectedDownloadLength) * downloadDataReceived)];
|
|
}
|
|
|
|
- (void)downloadDidFinish:(NSURLDownload *)download
|
|
{
|
|
[self installPackage];
|
|
}
|
|
|
|
- (void)download:(NSURLDownload *)download didFailWithError:(NSError *)error
|
|
{
|
|
[[NSAlert alertWithError:error] runModal];
|
|
}
|
|
|
|
- (void)nextStep:(id)sender
|
|
{
|
|
if (currentStep == 1) {
|
|
if ([[NSFileManager defaultManager] fileExistsAtPath:@"/Library/RubyMotion"]) {
|
|
if ([[NSAlert alertWithMessageText:@"RubyMotion Already Installed" defaultButton:@"Quit Installer" alternateButton:@"Force a New Installation" otherButton:@"" informativeTextWithFormat:@"It looks like RubyMotion has already been installed on this system. You can force a new installation if you want."] runModal] == NSAlertDefaultReturn) {
|
|
[[NSApplication sharedApplication] terminate:sender];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (currentStep == 3 && licenseKey == nil) {
|
|
NSString *givenLicenseKey = [licenseKeyTextField stringValue];
|
|
givenLicenseKey = [givenLicenseKey stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
|
bool licenseKeyValid = true;
|
|
if ([givenLicenseKey length] == 40) {
|
|
for (int i = 0, count = [givenLicenseKey length]; i < count; i++) {
|
|
UniChar c = [givenLicenseKey characterAtIndex:i];
|
|
c = tolower(c);
|
|
if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'))) {
|
|
licenseKeyValid = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
licenseKeyValid = false;
|
|
}
|
|
|
|
if (!licenseKeyValid) {
|
|
[self licenseKeyInvalid];
|
|
return;
|
|
}
|
|
|
|
|
|
NSString *post = [NSString stringWithFormat:@"product=rubymotion¤t_software_version=0.1&license_key=%@", givenLicenseKey];
|
|
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
|
|
NSString *postLength = [NSString stringWithFormat:@"%d", [postData length]];
|
|
|
|
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
|
|
[request setURL:[NSURL URLWithString:@"https://secure.rubymotion.com/update_software"]];
|
|
[request setHTTPMethod:@"POST"];
|
|
[request setValue:postLength forHTTPHeaderField:@"Content-Length"];
|
|
[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
|
|
[request setHTTPBody:postData];
|
|
|
|
[sender setEnabled:NO];
|
|
[licenseKeyTextField setEnabled:NO];
|
|
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
NSError *error = nil;
|
|
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&error];
|
|
|
|
[sender setEnabled:YES];
|
|
[licenseKeyTextField setEnabled:YES];
|
|
|
|
NSString *error_msg = nil;
|
|
if (error != nil) {
|
|
error_msg = [error localizedDescription];
|
|
}
|
|
else {
|
|
NSString *response_string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
|
if (response_string == nil) {
|
|
error_msg = @"Server response not properly encoded.";
|
|
}
|
|
else if ([response_string hasPrefix:@"http"]) {
|
|
[downloadURL release];
|
|
downloadURL = [NSURL URLWithString:response_string];
|
|
if (downloadURL == nil) {
|
|
error_msg = @"Server sent an incorrect download URL";
|
|
}
|
|
else {
|
|
[downloadURL retain];
|
|
[licenseKey release];
|
|
licenseKey = [givenLicenseKey copy];
|
|
[self nextStep:sender];
|
|
}
|
|
}
|
|
else {
|
|
error_msg = response_string;
|
|
}
|
|
}
|
|
if (error_msg != nil) {
|
|
[[NSAlert alertWithMessageText:@"License Key Validation Error" defaultButton:@"Okay" alternateButton:@"" otherButton:@"" informativeTextWithFormat:error_msg] runModal];
|
|
[self licenseKeyInvalid];
|
|
}
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (currentStep == 4 && !softwareInstalled) {
|
|
assert(downloadURL != nil);
|
|
NSURLRequest *request = [NSURLRequest requestWithURL:downloadURL];
|
|
if (download != nil) {
|
|
[download cancel];
|
|
[download release];
|
|
}
|
|
|
|
[installerPath release];
|
|
installerPath = [[NSTemporaryDirectory() stringByAppendingPathComponent:@"rubymotion.pkg"] copy];
|
|
[[NSFileManager defaultManager] removeItemAtPath:installerPath error:nil];
|
|
|
|
expectedDownloadLength = 0;
|
|
downloadDataReceived = 0;
|
|
|
|
download = [[NSURLDownload alloc] initWithRequest:request delegate:self];
|
|
[download setDestination:installerPath allowOverwrite:YES];
|
|
return;
|
|
}
|
|
|
|
NSView *oldView = [self viewForStep:currentStep];
|
|
NSView *newView = [self viewForStep:currentStep+1];
|
|
if (oldView == nil || newView == nil) {
|
|
return;
|
|
}
|
|
|
|
currentStep++;
|
|
|
|
NSRect oldFrame = [oldView frame];
|
|
NSRect newFrame = [newView frame];
|
|
|
|
if (newFrame.size.height != oldFrame.size.height) {
|
|
float delta = newFrame.size.height - oldFrame.size.height;
|
|
NSRect windowFrame = [[self window] frame];
|
|
windowFrame.origin.y -= (delta / 2.0);
|
|
windowFrame.size.height += delta;
|
|
[[self window] setFrame:windowFrame display:YES animate:YES];
|
|
|
|
newFrame.origin = oldFrame.origin;
|
|
newFrame.size.width = oldFrame.size.width;
|
|
}
|
|
|
|
[newView setFrame:newFrame];
|
|
|
|
[[[oldView superview] animator] replaceSubview:oldView with:newView];
|
|
|
|
switch (currentStep) {
|
|
case 1:
|
|
break;
|
|
case 2:
|
|
if (true) {
|
|
|
|
[seeLicenseButton setAttributedTitle:[[[NSAttributedString alloc] initWithString:@"software license agreement" attributes:[NSDictionary dictionaryWithObjectsAndKeys:[NSColor blueColor], NSForegroundColorAttributeName, [NSFont fontWithName:@"Lucida Grande" size:13.0], NSFontAttributeName, [NSNumber numberWithInt:NSUnderlineStyleSingle], NSUnderlineStyleAttributeName, nil]] autorelease]];
|
|
|
|
NSString *licensePath = [[NSBundle mainBundle] pathForResource:@"eula" ofType:@"rtf"];
|
|
assert(licensePath != nil);
|
|
NSData *data = [NSData dataWithContentsOfFile:licensePath];
|
|
NSAttributedString *licenseString = [[[NSAttributedString alloc] initWithRTF:data documentAttributes: NULL] autorelease];
|
|
[[licenseTextView textStorage] setAttributedString:licenseString];
|
|
}
|
|
break;
|
|
case 3:
|
|
break;
|
|
case 4:
|
|
[installProgressIndicator startAnimation:nil];
|
|
[installProgressStatusLabel setStringValue:@"Initializing..."];
|
|
[self performSelector:@selector(nextStep:) withObject:nil afterDelay:3];
|
|
break;
|
|
case 5:
|
|
break;
|
|
}
|
|
}
|
|
|
|
- (IBAction)seeLicense:(id)sender
|
|
{
|
|
NSString *licensePath = [[NSBundle mainBundle] pathForResource:@"eula" ofType:@"rtf"];
|
|
assert(licensePath != nil);
|
|
[[NSWorkspace sharedWorkspace] openFile:licensePath];
|
|
}
|
|
|
|
|
|
- (void)windowWillClose:(NSNotification *)notification
|
|
{
|
|
[[NSApplication sharedApplication] terminate:nil];
|
|
}
|
|
|
|
@end
|