#import "PithosSyncDaemon.h"
#import "ASIPithosRequest.h"
#import "ASIPithos.h"
-#import "ASIDownloadCache.h"
#import "LastCompletedSyncTransformer.h"
@implementation pithos_macosAppDelegate
-@synthesize pithosBrowserController, 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
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) {
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];
[self showPithosBrowser:self];
- pithosBrowserController.pithos = currentPithosAccount.pithos;
+ self.pithosBrowserController.pithos = currentPithosAccount.pithos;
} else {
// XXX maybe call specifically to go to new account tab
[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/
- (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];
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];
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];
- if (pithosPreferencesController && [pithosPreferencesController.selectedPithosAccount isEqualTo:pithosAccount]) {
- pithosPreferencesController.authUser = pithosAccount.authUser;
- pithosPreferencesController.authToken = pithosAccount.authToken;
+ if (self.pithosPreferencesController && [self.pithosPreferencesController.selectedPithosAccount isEqualTo:pithosAccount]) {
+ self.pithosPreferencesController.authUser = pithosAccount.authUser;
+ self.pithosPreferencesController.authToken = pithosAccount.authToken;
}
self.activated = YES;
if ([pithosAccount isEqualTo:currentPithosAccount]) {
[self showPithosBrowser:self];
- pithosBrowserController.pithos = pithosAccount.pithos;
+ self.pithosBrowserController.pithos = pithosAccount.pithos;
}
}
// XXX else maybe show an error message?
- (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"];
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 {
+ if (!pithosBrowserController) {
+ pithosBrowserController = [[PithosBrowserController alloc] init];
+ }
+ return pithosBrowserController;
+}
+
+- (PithosPreferencesController *)pithosPreferencesController {
+ if (!pithosPreferencesController) {
+ pithosPreferencesController = [[PithosPreferencesController alloc] init];
+ }
+ 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
- (void)menuNeedsUpdate:(NSMenu *)menu {
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)];
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
// [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:@""]];
}
}
- (IBAction)showPithosBrowser:(id)sender {
if (!activated)
return;
- [pithosBrowserController showWindow:sender];
- [[pithosBrowserController window] makeKeyAndOrderFront:sender];
+ [self.pithosBrowserController showWindow:sender];
+ [[self.pithosBrowserController window] makeKeyAndOrderFront:sender];
[NSApp activateIgnoringOtherApps:YES];
}
- (IBAction)showPithosPreferences:(id)sender {
- [pithosPreferencesController showWindow:sender];
- [[pithosPreferencesController window] makeKeyAndOrderFront:sender];
+ [self.pithosPreferencesController showWindow:sender];
+ [[self.pithosPreferencesController window] makeKeyAndOrderFront:sender];
[NSApp activateIgnoringOtherApps:YES];
}
for (PithosAccount *pithosAccount in pithosAccounts) {
if (pithosAccount.active) {
self.currentPithosAccount = pithosAccount;
- pithosBrowserController.pithos = currentPithosAccount.pithos;
+ self.pithosBrowserController.pithosAccountManager = currentPithosAccount;
+ self.pithosBrowserController.pithos = currentPithosAccount.pithos;
break;
}
}
if ([self.currentPithosAccount isEqualTo:removedPithosAccount]) {
self.activated = NO;
- [pithosBrowserController.window close];
- [pithosBrowserController resetBrowser];
+ [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
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"];
return;
}
self.currentPithosAccount = pithosAccount;
+ self.pithosBrowserController.pithosAccountManager = currentPithosAccount;
[self showPithosBrowser:self];
- pithosBrowserController.pithos = currentPithosAccount.pithos;
+ self.pithosBrowserController.pithos = currentPithosAccount.pithos;
}
}
// if (!pithosAccount.active)
// return;
// pithosAccount.syncActive = !pithosAccount.syncActive;
-// if (pithosPreferencesController && [pithosPreferencesController.selectedPithosAccount isEqualTo:pithosAccount])
-// pithosPreferencesController.syncActive = pithosAccount.syncActive;
+// if (self.pithosPreferencesController && [self.pithosPreferencesController.selectedPithosAccount isEqualTo:pithosAccount])
+// self.pithosPreferencesController.syncActive = pithosAccount.syncActive;
// [self savePithosAccounts:self];
//}
+- (IBAction)menuCheckForUpdates:(NSMenuItem *)sender {
+ @synchronized(self) {
+ checkForUpdatesCalledFromMenu = YES;
+ }
+ [self checkForUpdates];
+}
+
@end