X-Git-Url: https://code.grnet.gr/git/pithos-macos/blobdiff_plain/904acde2af10b0b059ee38d92e175a6f2542c589..97c9a632d23b88952081fc38a9e2bc03eb2a7ac9:/pithos-macos/pithos_macosAppDelegate.m diff --git a/pithos-macos/pithos_macosAppDelegate.m b/pithos-macos/pithos_macosAppDelegate.m index 151af90..95d22ae 100644 --- a/pithos-macos/pithos_macosAppDelegate.m +++ b/pithos-macos/pithos_macosAppDelegate.m @@ -42,12 +42,11 @@ #import "PithosSyncDaemon.h" #import "ASIPithosRequest.h" #import "ASIPithos.h" -#import "ASIDownloadCache.h" #import "LastCompletedSyncTransformer.h" @implementation pithos_macosAppDelegate -@synthesize pithosBrowserController, pithosPreferencesController, alwaysNo, activated, -currentPithosAccount, pithosAccounts, pithosAccountsDictionary, syncPithosAccount, activityFacilityTimeInterval; +@synthesize pithosBrowserController, pithosPreferencesController, alwaysNo, openAtLoginEnabled, openAtLogin, activated, +currentPithosAccount, pithosAccounts, pithosAccountsDictionary, syncPithosAccount, activityFacilityTimeInterval, checkForUpdatesNotRunning; - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { [[NSAppleEventManager sharedAppleEventManager] setEventHandler:self @@ -55,7 +54,16 @@ currentPithosAccount, pithosAccounts, pithosAccountsDictionary, syncPithosAccoun forEventClass:kInternetEventClass andEventID:kAEGetURL]; - userDefaults = [[NSUserDefaults standardUserDefaults] retain]; + // Based on: https://github.com/Mozketo/LaunchAtLoginController + // and: http://cocoatutorial.grapewave.com/2010/02/creating-andor-removing-a-login-item/ + loginItems = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL); + if (loginItems) { + LSSharedFileListAddObserver(loginItems, CFRunLoopGetMain(), (CFStringRef)NSDefaultRunLoopMode, LSSharedFileListChanged, (__bridge void *)(self)); + LSSharedFileListChanged(loginItems, (__bridge void *)(self)); + self.openAtLoginEnabled = YES; + } + + userDefaults = [NSUserDefaults standardUserDefaults]; syncTimeInterval = [userDefaults doubleForKey:@"syncTimeInterval"]; if (syncTimeInterval <= 0.0) { @@ -89,10 +97,11 @@ currentPithosAccount, pithosAccounts, pithosAccountsDictionary, syncPithosAccoun for (PithosAccount *pithosAccount in pithosAccounts) { [pithosAccountsDictionary setObject:pithosAccount forKey:pithosAccount.name]; if (!currentPithosAccount && pithosAccount.active) - currentPithosAccount = [pithosAccount retain]; + self.currentPithosAccount = pithosAccount; } if (!currentPithosAccount) self.currentPithosAccount = [pithosAccounts objectAtIndex:0]; + self.pithosBrowserController.pithosAccountManager = currentPithosAccount; if (currentPithosAccount.active) { [self savePithosAccounts:self]; @@ -103,12 +112,17 @@ currentPithosAccount, pithosAccounts, pithosAccountsDictionary, syncPithosAccoun [self showPithosPreferences:self]; } - syncTimer = [[NSTimer scheduledTimerWithTimeInterval:syncTimeInterval + syncTimer = [NSTimer scheduledTimerWithTimeInterval:syncTimeInterval target:self selector:@selector(sync) userInfo:nil - repeats:YES] retain]; + repeats:YES]; [syncTimer fire]; + + @synchronized(self) { + self.checkForUpdatesNotRunning = YES; + } + [self checkForUpdates]; } // Based on: http://cocoatutorial.grapewave.com/2010/01/creating-a-status-bar-application/ @@ -116,14 +130,14 @@ currentPithosAccount, pithosAccounts, pithosAccountsDictionary, syncPithosAccoun - (void)awakeFromNib { NSImage *sourceImage = [NSImage imageNamed:@"pithos-large.png"]; - NSImage *smallImage = [[[NSImage alloc] initWithSize:NSMakeSize(18, 18)] autorelease]; + NSImage *smallImage = [[NSImage alloc] initWithSize:NSMakeSize(18, 18)]; [smallImage lockFocus]; [[NSGraphicsContext currentContext] setImageInterpolation:NSImageInterpolationHigh]; [sourceImage setSize:NSMakeSize(18, 18)]; - [sourceImage compositeToPoint:NSZeroPoint operation:NSCompositeCopy]; + [sourceImage drawAtPoint:NSZeroPoint fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0]; [smallImage unlockFocus]; - statusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength] retain]; + statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength]; [statusItem setMenu:statusMenu]; [statusItem setImage:sourceImage]; [statusItem setHighlightMode:YES]; @@ -135,10 +149,9 @@ currentPithosAccount, pithosAccounts, pithosAccountsDictionary, syncPithosAccoun NSURL *url = [NSURL URLWithString:[[event paramDescriptorForKeyword:keyDirectObject] stringValue]]; NSString *host = [url host]; NSString *query = [url query]; - PithosAccount *pithosAccount = [pithosAccountsDictionary objectForKey:[url lastPathComponent]]; + PithosAccount *pithosAccount = [pithosAccountsDictionary objectForKey:[ASIPithosRequest decodeFromPercentEscape:[url lastPathComponent]]]; NSProcessInfo *processInfo = [NSProcessInfo processInfo]; - if ([host isEqualToString:[NSString stringWithFormat:@"%@_%d", [processInfo processName], [processInfo processIdentifier]]] && - pithosAccount && query) { + if ([host isEqualToString:[NSString stringWithFormat:@"%d", [processInfo processIdentifier]]] && pithosAccount && query) { // user= NSString *authUser; NSRange userRange = [query rangeOfString:@"user=" options:NSCaseInsensitiveSearch]; @@ -172,7 +185,7 @@ currentPithosAccount, pithosAccounts, pithosAccountsDictionary, syncPithosAccoun stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; } - NSLog(@"query authUser: '%@', authToken: '%@'", authUser, authToken); + DLog(@"query authUser: '%@', authToken: '%@'", authUser, authToken); if ([authUser length] && [authToken length]) { [pithosAccount authenticateWithServerURL:nil authUser:authUser authToken:authToken]; [self savePithosAccounts:self]; @@ -194,7 +207,7 @@ currentPithosAccount, pithosAccounts, pithosAccountsDictionary, syncPithosAccoun - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender { [self savePithosAccounts:self]; if ([self.pithosBrowserController operationsPending]) { - NSAlert *alert = [[[NSAlert alloc] init] autorelease]; + NSAlert *alert = [[NSAlert alloc] init]; [alert setMessageText:@"Pending Operations"]; [alert setInformativeText:@"There are pending operations in the browser, do you want to quit and cancel them?"]; [alert addButtonWithTitle:@"OK"]; @@ -203,10 +216,46 @@ currentPithosAccount, pithosAccounts, pithosAccountsDictionary, syncPithosAccoun if (choice == NSAlertSecondButtonReturn) return NSTerminateCancel; } + if (loginItems) { + LSSharedFileListRemoveObserver(loginItems, CFRunLoopGetMain(), (CFStringRef)NSDefaultRunLoopMode, LSSharedFileListChanged, (__bridge void *)(self)); + CFRelease(loginItems); + } return NSTerminateNow; } #pragma mark - +#pragma mark Callbacks + +- (void)loginItemsChanged { + NSURL *appURL = [[NSBundle mainBundle] bundleURL]; + LSSharedFileListItemRef appItem = NULL; + NSArray *snapshot = (__bridge_transfer NSArray *)(LSSharedFileListCopySnapshot(loginItems, NULL)); + for (id itemObject in snapshot) { + LSSharedFileListItemRef item = (__bridge LSSharedFileListItemRef)itemObject; + UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes; + CFURLRef currentItemURL = NULL; + LSSharedFileListItemResolve(item, resolutionFlags, ¤tItemURL, NULL); + if (currentItemURL && CFEqual(currentItemURL, (__bridge CFTypeRef)(appURL))) { + CFRelease(currentItemURL); + appItem = item; + break; + } + if (currentItemURL) + CFRelease(currentItemURL); + } + + if (appItem && (!openAtLogin || !openAtLoginEnabled)) + self.openAtLogin = YES; + else if (!appItem && (openAtLogin || !openAtLoginEnabled)) + self.openAtLogin = NO; +} + +void LSSharedFileListChanged(LSSharedFileListRef inList, void *context) { + pithos_macosAppDelegate *self = (__bridge id)context; + [self loginItemsChanged]; +} + +#pragma mark - #pragma mark Properties - (PithosBrowserController *)pithosBrowserController { @@ -223,6 +272,39 @@ currentPithosAccount, pithosAccounts, pithosAccountsDictionary, syncPithosAccoun return pithosPreferencesController; } +- (void)setOpenAtLogin:(BOOL)anOpenAtLogin { + if (!openAtLoginEnabled) { + openAtLogin = anOpenAtLogin; + } else if (anOpenAtLogin != openAtLogin) { + NSURL *appURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]]; + LSSharedFileListItemRef appItem = NULL; + NSArray *snapshot = (__bridge_transfer NSArray *)(LSSharedFileListCopySnapshot(loginItems, NULL)); + for (id itemObject in snapshot) { + LSSharedFileListItemRef item = (__bridge LSSharedFileListItemRef)itemObject; + UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes; + CFURLRef currentItemURL = NULL; + LSSharedFileListItemResolve(item, resolutionFlags, ¤tItemURL, NULL); + if (currentItemURL && CFEqual(currentItemURL, (__bridge CFTypeRef)(appURL))) { + CFRelease(currentItemURL); + appItem = item; + break; + } + if (currentItemURL) + CFRelease(currentItemURL); + } + + if (anOpenAtLogin) { + if (!appItem) + LSSharedFileListInsertItemURL(loginItems, kLSSharedFileListItemBeforeFirst, NULL, NULL, (__bridge CFURLRef)appURL, NULL, NULL); + openAtLogin = YES; + } else { + if (appItem) + LSSharedFileListItemRemove(loginItems, appItem); + openAtLogin = NO; + } + } +} + #pragma mark - #pragma mark NSMenuDelegate @@ -232,9 +314,9 @@ currentPithosAccount, pithosAccounts, pithosAccountsDictionary, syncPithosAccoun if ([menu isEqualTo:accountsMenu]) { [menu setAutoenablesItems:NO]; for (PithosAccount *pithosAccount in pithosAccounts) { - menuItem = [[[NSMenuItem alloc] initWithTitle:pithosAccount.name + menuItem = [[NSMenuItem alloc] initWithTitle:pithosAccount.name action:@selector(menuChangePithosAccount:) - keyEquivalent:@""] autorelease]; + keyEquivalent:@""]; [menuItem setRepresentedObject:pithosAccount]; [menuItem setEnabled:pithosAccount.active]; [menuItem setState:((pithosAccount.active && [currentPithosAccount isEqualTo:pithosAccount]) ? NSOnState : NSOffState)]; @@ -246,7 +328,7 @@ currentPithosAccount, pithosAccounts, pithosAccountsDictionary, syncPithosAccoun for (PithosAccount *pithosAccount in pithosAccounts) { menuItemTitle = [NSString stringWithFormat:@"%@: %@", pithosAccount.name, - [[[[LastCompletedSyncTransformer alloc] init] autorelease] transformedValue:pithosAccount.syncLastCompleted]]; + [[[LastCompletedSyncTransformer alloc] init] transformedValue:pithosAccount.syncLastCompleted]]; if ([pithosAccount isEqualTo:syncPithosAccount] && [pithosAccount.syncDaemon isSyncing]) menuItemTitle = [menuItemTitle stringByAppendingString:@" (syncing)"]; // menuItem = [[[NSMenuItem alloc] initWithTitle:menuItemTitle @@ -255,15 +337,15 @@ currentPithosAccount, pithosAccounts, pithosAccountsDictionary, syncPithosAccoun // [menuItem setRepresentedObject:pithosAccount]; // [menuItem setEnabled:pithosAccount.active]; // [menuItem setState:((pithosAccount.active && pithosAccount.syncActive) ? NSOnState : NSOffState)]; - menuItem = [[[NSMenuItem alloc] initWithTitle:menuItemTitle action:nil keyEquivalent:@""] autorelease]; + menuItem = [[NSMenuItem alloc] initWithTitle:menuItemTitle action:nil keyEquivalent:@""]; [menuItem setEnabled:NO]; [menuItem setState:NO]; [menu addItem:menuItem]; } [menu addItem:[NSMenuItem separatorItem]]; - [menu addItem:[[[NSMenuItem alloc] initWithTitle:@"Next Sync" + [menu addItem:[[NSMenuItem alloc] initWithTitle:@"Next Sync" action:@selector(sync) - keyEquivalent:@""] autorelease]]; + keyEquivalent:@""]]; } } @@ -284,6 +366,11 @@ currentPithosAccount, pithosAccounts, pithosAccountsDictionary, syncPithosAccoun [NSApp activateIgnoringOtherApps:YES]; } +- (IBAction)showPithosAbout:(id)sender { + [NSApp orderFrontStandardAboutPanel:sender]; + [NSApp activateIgnoringOtherApps:YES]; +} + - (void)sync { if (!activated || ![pithosAccounts count]) return; @@ -362,6 +449,7 @@ currentPithosAccount, pithosAccounts, pithosAccountsDictionary, syncPithosAccoun for (PithosAccount *pithosAccount in pithosAccounts) { if (pithosAccount.active) { self.currentPithosAccount = pithosAccount; + self.pithosBrowserController.pithosAccountManager = currentPithosAccount; self.pithosBrowserController.pithos = currentPithosAccount.pithos; break; } @@ -371,12 +459,86 @@ currentPithosAccount, pithosAccounts, pithosAccountsDictionary, syncPithosAccoun [self.pithosBrowserController.window close]; [self.pithosBrowserController resetBrowser]; self.currentPithosAccount = [pithosAccounts objectAtIndex:0]; + self.pithosBrowserController.pithosAccountManager = currentPithosAccount; } } if ([self.syncPithosAccount isEqualTo:removedPithosAccount]) self.syncPithosAccount = nil; } +- (void)checkForUpdates { + @synchronized(self) { + if (!checkForUpdatesNotRunning) + return; + self.checkForUpdatesNotRunning = NO; + } + ASIHTTPRequest *checkForUpdatesRequest = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"https://code.grnet.gr/projects/pithos-macos/repository/revisions/master/raw/pithos-macos/pithos-macos-Info.plist"]]; + checkForUpdatesRequest.delegate = self; + checkForUpdatesRequest.didFinishSelector = @selector(checkForUpdatesRequestFinished:); + checkForUpdatesRequest.didFailSelector = @selector(checkForUpdatesRequestFailed:); + checkForUpdatesRequest.timeOutSeconds = 60; + checkForUpdatesRequest.numberOfTimesToRetryOnTimeout = 10; + [checkForUpdatesRequest startAsynchronous]; +} + +#pragma mark - +#pragma mark ASIHTTPRequestDelegate + +- (void)checkForUpdatesRequestFinished:(ASIHTTPRequest *)request { + if (request.responseStatusCode == 200) { + NSError *error = nil; + NSDictionary *plistDictionary = [NSPropertyListSerialization propertyListWithData:[request.responseString dataUsingEncoding:NSUTF8StringEncoding] + options:NSPropertyListImmutable + format:NULL + error:&error]; + if (!error) { + NSString *currentVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]; + NSString *newVersion = [plistDictionary objectForKey:@"CFBundleVersion"]; + NSURL *distributionURL = [NSURL URLWithString:[plistDictionary objectForKey:@"PithosDistributionURL"]]; + if (newVersion && currentVersion && distributionURL && ([newVersion doubleValue] > [currentVersion doubleValue])) { + NSAlert *alert = [[NSAlert alloc] init]; + [alert setMessageText:@"Updates Available"]; + [alert setInformativeText:@"An updated version is available"]; + [alert addButtonWithTitle:@"Download"]; + [alert runModal]; + [[NSWorkspace sharedWorkspace] openURL:distributionURL]; + } else if (checkForUpdatesCalledFromMenu) { + NSAlert *alert = [[NSAlert alloc] init]; + [alert setMessageText:@"No Updates Available"]; + [alert setInformativeText:@"You are running the latest version"]; + [alert addButtonWithTitle:@"OK"]; + [alert runModal]; + checkForUpdatesCalledFromMenu = NO; + } + [NSTimer scheduledTimerWithTimeInterval:86400 + target:self + selector:@selector(checkForUpdates) + userInfo:nil + repeats:YES]; + self.checkForUpdatesNotRunning = YES; + return; + } + DLog(@"Check for update plist error: %@", error); + } +} + +- (void)checkForUpdatesRequestFailed:(ASIHTTPRequest *)request { + if (checkForUpdatesCalledFromMenu) { + NSAlert *alert = [[NSAlert alloc] init]; + [alert setMessageText:@"Check for Updates Error"]; + [alert setInformativeText:@"Cannot check for updates now, try again later"]; + [alert addButtonWithTitle:@"OK"]; + [alert runModal]; + checkForUpdatesCalledFromMenu = NO; + } + [NSTimer scheduledTimerWithTimeInterval:600 + target:self + selector:@selector(checkForUpdates) + userInfo:nil + repeats:YES]; + self.checkForUpdatesNotRunning = YES; +} + #pragma mark - #pragma mark Menu Actions @@ -386,7 +548,7 @@ currentPithosAccount, pithosAccounts, pithosAccountsDictionary, syncPithosAccoun return; if (![currentPithosAccount isEqualTo:pithosAccount] && [pithosAccounts containsObject:pithosAccount]) { if ([self.pithosBrowserController operationsPending]) { - NSAlert *alert = [[[NSAlert alloc] init] autorelease]; + NSAlert *alert = [[NSAlert alloc] init]; [alert setMessageText:@"Pending Operations"]; [alert setInformativeText:@"There are pending operations in the browser, do you want to change accounts and cancel them?"]; [alert addButtonWithTitle:@"OK"]; @@ -396,6 +558,7 @@ currentPithosAccount, pithosAccounts, pithosAccountsDictionary, syncPithosAccoun return; } self.currentPithosAccount = pithosAccount; + self.pithosBrowserController.pithosAccountManager = currentPithosAccount; [self showPithosBrowser:self]; self.pithosBrowserController.pithos = currentPithosAccount.pithos; } @@ -411,4 +574,11 @@ currentPithosAccount, pithosAccounts, pithosAccountsDictionary, syncPithosAccoun // [self savePithosAccounts:self]; //} +- (IBAction)menuCheckForUpdates:(NSMenuItem *)sender { + @synchronized(self) { + checkForUpdatesCalledFromMenu = YES; + } + [self checkForUpdates]; +} + @end