root / pithos-macos / pithos_macosAppDelegate.m @ fb27f368
History | View | Annotate | Download (26.5 kB)
1 |
// |
---|---|
2 |
// pithos_macosAppDelegate.m |
3 |
// pithos-macos |
4 |
// |
5 |
// Copyright 2011-2013 GRNET S.A. All rights reserved. |
6 |
// |
7 |
// Redistribution and use in source and binary forms, with or |
8 |
// without modification, are permitted provided that the following |
9 |
// conditions are met: |
10 |
// |
11 |
// 1. Redistributions of source code must retain the above |
12 |
// copyright notice, this list of conditions and the following |
13 |
// disclaimer. |
14 |
// |
15 |
// 2. Redistributions in binary form must reproduce the above |
16 |
// copyright notice, this list of conditions and the following |
17 |
// disclaimer in the documentation and/or other materials |
18 |
// provided with the distribution. |
19 |
// |
20 |
// THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS |
21 |
// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
22 |
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
23 |
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR |
24 |
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
25 |
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
26 |
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
27 |
// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
28 |
// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
29 |
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
30 |
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
31 |
// POSSIBILITY OF SUCH DAMAGE. |
32 |
// |
33 |
// The views and conclusions contained in the software and |
34 |
// documentation are those of the authors and should not be |
35 |
// interpreted as representing official policies, either expressed |
36 |
// or implied, of GRNET S.A. |
37 |
|
38 |
#import "pithos_macosAppDelegate.h" |
39 |
#import "PithosAccount.h" |
40 |
#import "PithosBrowserController.h" |
41 |
#import "PithosPreferencesController.h" |
42 |
#import "PithosSyncDaemon.h" |
43 |
#import "ASIPithosRequest.h" |
44 |
#import "ASIPithos.h" |
45 |
#import "PithosUtilities.h" |
46 |
#import "LastCompletedSyncTransformer.h" |
47 |
|
48 |
@implementation pithos_macosAppDelegate |
49 |
@synthesize pithosBrowserController, pithosPreferencesController, alwaysNo, openAtLoginEnabled, openAtLogin, activated, |
50 |
currentPithosAccount, pithosAccounts, pithosAccountsDictionary, syncPithosAccount, activityFacilityTimeInterval, checkForUpdatesNotRunning; |
51 |
|
52 |
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { |
53 |
[[NSAppleEventManager sharedAppleEventManager] setEventHandler:self |
54 |
andSelector:@selector(handleAppleEvent:withReplyEvent:) |
55 |
forEventClass:kInternetEventClass |
56 |
andEventID:kAEGetURL]; |
57 |
|
58 |
// Based on: https://github.com/Mozketo/LaunchAtLoginController |
59 |
// and: http://cocoatutorial.grapewave.com/2010/02/creating-andor-removing-a-login-item/ |
60 |
loginItems = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL); |
61 |
if (loginItems) { |
62 |
LSSharedFileListAddObserver(loginItems, CFRunLoopGetMain(), (CFStringRef)NSDefaultRunLoopMode, LSSharedFileListChanged, (__bridge void *)(self)); |
63 |
LSSharedFileListChanged(loginItems, (__bridge void *)(self)); |
64 |
self.openAtLoginEnabled = YES; |
65 |
} |
66 |
|
67 |
userDefaults = [NSUserDefaults standardUserDefaults]; |
68 |
|
69 |
syncTimeInterval = [userDefaults doubleForKey:@"syncTimeInterval"]; |
70 |
if (syncTimeInterval <= 0.0) { |
71 |
syncTimeInterval = 180.0; |
72 |
[userDefaults setDouble:syncTimeInterval forKey:@"syncTimeInterval"]; |
73 |
[userDefaults synchronize]; |
74 |
} |
75 |
|
76 |
activityFacilityTimeInterval = [userDefaults doubleForKey:@"activityFacilityTimeInterval"]; |
77 |
if (activityFacilityTimeInterval <= 0.0) { |
78 |
activityFacilityTimeInterval = 0.05; |
79 |
[userDefaults setDouble:activityFacilityTimeInterval forKey:@"activityFacilityTimeInterval"]; |
80 |
[userDefaults synchronize]; |
81 |
} |
82 |
|
83 |
NSData *tmpData = [userDefaults objectForKey:@"pithosAccounts"]; |
84 |
NSArray *tmpArray; |
85 |
if (tmpData && (tmpArray = [NSKeyedUnarchiver unarchiveObjectWithData:tmpData])) |
86 |
self.pithosAccounts = [NSMutableArray arrayWithArray:tmpArray]; |
87 |
else |
88 |
self.pithosAccounts = [NSMutableArray array]; |
89 |
|
90 |
if (![pithosAccounts count]) { |
91 |
[pithosAccounts addObject:[PithosAccount pithosAccount]]; |
92 |
self.pithosAccounts = self.pithosAccounts; |
93 |
} else { |
94 |
self.activated = YES; |
95 |
} |
96 |
|
97 |
pithosAccountsDictionary = [[NSMutableDictionary alloc] initWithCapacity:[pithosAccounts count]]; |
98 |
for (PithosAccount *pithosAccount in pithosAccounts) { |
99 |
[pithosAccountsDictionary setObject:pithosAccount forKey:pithosAccount.name]; |
100 |
if (pithosAccount.active) { |
101 |
if (!pithosAccount.manual) { |
102 |
ASIPithosRequest *serviceCatalogRequest = [ASIPithosRequest serviceCatalogRequestWithPithos:pithosAccount.pithos]; |
103 |
[PithosUtilities startAndWaitForRequest:serviceCatalogRequest]; |
104 |
[pithosAccount updateServicesFromServiceCatalogRequest:serviceCatalogRequest]; |
105 |
} |
106 |
if (!currentPithosAccount) |
107 |
self.currentPithosAccount = pithosAccount; |
108 |
} |
109 |
} |
110 |
if (!currentPithosAccount) |
111 |
self.currentPithosAccount = [pithosAccounts objectAtIndex:0]; |
112 |
|
113 |
if (currentPithosAccount.active) { |
114 |
[self savePithosAccounts:self]; |
115 |
[self showPithosBrowser:self]; |
116 |
self.pithosBrowserController.pithosAccountManager = currentPithosAccount; |
117 |
} else { |
118 |
// XXX maybe call specifically to go to new account tab |
119 |
[self showPithosPreferences:self]; |
120 |
} |
121 |
|
122 |
syncTimer = [NSTimer scheduledTimerWithTimeInterval:syncTimeInterval |
123 |
target:self |
124 |
selector:@selector(sync) |
125 |
userInfo:nil |
126 |
repeats:YES]; |
127 |
[syncTimer fire]; |
128 |
|
129 |
@synchronized(self) { |
130 |
self.checkForUpdatesNotRunning = YES; |
131 |
} |
132 |
[self checkForUpdates]; |
133 |
} |
134 |
|
135 |
// Based on: http://cocoatutorial.grapewave.com/2010/01/creating-a-status-bar-application/ |
136 |
// and: http://www.cocoadev.com/index.pl?ThumbnailImages |
137 |
- (void)awakeFromNib { |
138 |
NSImage *sourceImage = [NSImage imageNamed:@"pithos-large.png"]; |
139 |
|
140 |
NSImage *smallImage = [[NSImage alloc] initWithSize:NSMakeSize(18, 18)]; |
141 |
[smallImage lockFocus]; |
142 |
[[NSGraphicsContext currentContext] setImageInterpolation:NSImageInterpolationHigh]; |
143 |
[sourceImage setSize:NSMakeSize(18, 18)]; |
144 |
[sourceImage drawAtPoint:NSZeroPoint fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0]; |
145 |
[smallImage unlockFocus]; |
146 |
|
147 |
statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength]; |
148 |
[statusItem setMenu:statusMenu]; |
149 |
[statusItem setImage:sourceImage]; |
150 |
[statusItem setHighlightMode:YES]; |
151 |
|
152 |
self.alwaysNo = NO; |
153 |
} |
154 |
|
155 |
- (void)handleAppleEvent:(NSAppleEventDescriptor *)event withReplyEvent: (NSAppleEventDescriptor *)replyEvent { |
156 |
NSURL *url = [NSURL URLWithString:[[event paramDescriptorForKeyword:keyDirectObject] stringValue]]; |
157 |
NSString *host = [url host]; |
158 |
NSString *query = [url query]; |
159 |
PithosAccount *pithosAccount = [pithosAccountsDictionary objectForKey:[ASIPithosRequest decodeFromPercentEscape:[url lastPathComponent]]]; |
160 |
NSProcessInfo *processInfo = [NSProcessInfo processInfo]; |
161 |
if ([host isEqualToString:[NSString stringWithFormat:@"%d", [processInfo processIdentifier]]] && pithosAccount && query) { |
162 |
// user= |
163 |
// optional |
164 |
NSString *authUser = nil; |
165 |
NSRange userRange = [query rangeOfString:@"user=" options:NSCaseInsensitiveSearch]; |
166 |
if (userRange.length) { |
167 |
NSUInteger authUserStartLocation = userRange.location + userRange.length; |
168 |
NSRange userEndRange = [query rangeOfString:@"&" options:NSCaseInsensitiveSearch |
169 |
range:NSMakeRange(authUserStartLocation, [query length] - authUserStartLocation)]; |
170 |
if (userEndRange.length) { |
171 |
authUser = [[query substringWithRange:NSMakeRange(authUserStartLocation, userEndRange.location - authUserStartLocation)] |
172 |
stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; |
173 |
} else { |
174 |
authUser = [[query substringFromIndex:authUserStartLocation] |
175 |
stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; |
176 |
} |
177 |
} |
178 |
// token= |
179 |
// required |
180 |
NSString *authToken = nil; |
181 |
NSRange tokenRange = [query rangeOfString:@"token=" options:NSCaseInsensitiveSearch]; |
182 |
if (tokenRange.length == 0) |
183 |
// XXX maybe show an error message? |
184 |
return; |
185 |
NSUInteger authTokenStartLocation = tokenRange.location + tokenRange.length; |
186 |
NSRange tokenEndRange = [query rangeOfString:@"&" options:NSCaseInsensitiveSearch |
187 |
range:NSMakeRange(authTokenStartLocation, [query length] - authTokenStartLocation)]; |
188 |
if (tokenEndRange.length) { |
189 |
authToken = [[query substringWithRange:NSMakeRange(authTokenStartLocation, tokenEndRange.location - authTokenStartLocation)] |
190 |
stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; |
191 |
} else { |
192 |
authToken = [[query substringFromIndex:authTokenStartLocation] |
193 |
stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; |
194 |
} |
195 |
|
196 |
DLog(@"query authUser: '%@', authToken: '%@'", authUser, authToken); |
197 |
if (authToken || authUser) { |
198 |
if (self.pithosPreferencesController && [self.pithosPreferencesController.selectedPithosAccount isEqualTo:pithosAccount]) { |
199 |
self.pithosPreferencesController.authToken = authToken; |
200 |
self.pithosPreferencesController.authUser = authUser; |
201 |
[self showPithosPreferences:self]; |
202 |
} |
203 |
} |
204 |
// XXX else maybe show an error message? |
205 |
} |
206 |
// XXX else maybe show an error message? |
207 |
} |
208 |
|
209 |
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender { |
210 |
[self savePithosAccounts:self]; |
211 |
if ([self.pithosBrowserController operationsPending]) { |
212 |
NSAlert *alert = [[NSAlert alloc] init]; |
213 |
[alert setMessageText:@"Pending Operations"]; |
214 |
[alert setInformativeText:@"There are pending operations in the browser, do you want to quit and cancel them?"]; |
215 |
[alert addButtonWithTitle:@"OK"]; |
216 |
[alert addButtonWithTitle:@"Cancel"]; |
217 |
NSInteger choice = [alert runModal]; |
218 |
if (choice == NSAlertSecondButtonReturn) |
219 |
return NSTerminateCancel; |
220 |
} |
221 |
if (loginItems) { |
222 |
LSSharedFileListRemoveObserver(loginItems, CFRunLoopGetMain(), (CFStringRef)NSDefaultRunLoopMode, LSSharedFileListChanged, (__bridge void *)(self)); |
223 |
CFRelease(loginItems); |
224 |
} |
225 |
return NSTerminateNow; |
226 |
} |
227 |
|
228 |
#pragma mark - |
229 |
#pragma mark Callbacks |
230 |
|
231 |
- (void)loginItemsChanged { |
232 |
NSURL *appURL = [[NSBundle mainBundle] bundleURL]; |
233 |
LSSharedFileListItemRef appItem = NULL; |
234 |
NSArray *snapshot = (__bridge_transfer NSArray *)(LSSharedFileListCopySnapshot(loginItems, NULL)); |
235 |
for (id itemObject in snapshot) { |
236 |
LSSharedFileListItemRef item = (__bridge LSSharedFileListItemRef)itemObject; |
237 |
UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes; |
238 |
CFURLRef currentItemURL = NULL; |
239 |
LSSharedFileListItemResolve(item, resolutionFlags, ¤tItemURL, NULL); |
240 |
if (currentItemURL && CFEqual(currentItemURL, (__bridge CFTypeRef)(appURL))) { |
241 |
CFRelease(currentItemURL); |
242 |
appItem = item; |
243 |
break; |
244 |
} |
245 |
if (currentItemURL) |
246 |
CFRelease(currentItemURL); |
247 |
} |
248 |
|
249 |
if (appItem && (!openAtLogin || !openAtLoginEnabled)) |
250 |
self.openAtLogin = YES; |
251 |
else if (!appItem && (openAtLogin || !openAtLoginEnabled)) |
252 |
self.openAtLogin = NO; |
253 |
} |
254 |
|
255 |
void LSSharedFileListChanged(LSSharedFileListRef inList, void *context) { |
256 |
pithos_macosAppDelegate *self = (__bridge id)context; |
257 |
[self loginItemsChanged]; |
258 |
} |
259 |
|
260 |
#pragma mark - |
261 |
#pragma mark Properties |
262 |
|
263 |
- (PithosBrowserController *)pithosBrowserController { |
264 |
if (!pithosBrowserController) { |
265 |
pithosBrowserController = [[PithosBrowserController alloc] init]; |
266 |
} |
267 |
return pithosBrowserController; |
268 |
} |
269 |
|
270 |
- (PithosPreferencesController *)pithosPreferencesController { |
271 |
if (!pithosPreferencesController) { |
272 |
pithosPreferencesController = [[PithosPreferencesController alloc] init]; |
273 |
} |
274 |
return pithosPreferencesController; |
275 |
} |
276 |
|
277 |
- (void)setOpenAtLogin:(BOOL)anOpenAtLogin { |
278 |
if (!openAtLoginEnabled) { |
279 |
openAtLogin = anOpenAtLogin; |
280 |
} else if (anOpenAtLogin != openAtLogin) { |
281 |
NSURL *appURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]]; |
282 |
LSSharedFileListItemRef appItem = NULL; |
283 |
NSArray *snapshot = (__bridge_transfer NSArray *)(LSSharedFileListCopySnapshot(loginItems, NULL)); |
284 |
for (id itemObject in snapshot) { |
285 |
LSSharedFileListItemRef item = (__bridge LSSharedFileListItemRef)itemObject; |
286 |
UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes; |
287 |
CFURLRef currentItemURL = NULL; |
288 |
LSSharedFileListItemResolve(item, resolutionFlags, ¤tItemURL, NULL); |
289 |
if (currentItemURL && CFEqual(currentItemURL, (__bridge CFTypeRef)(appURL))) { |
290 |
CFRelease(currentItemURL); |
291 |
appItem = item; |
292 |
break; |
293 |
} |
294 |
if (currentItemURL) |
295 |
CFRelease(currentItemURL); |
296 |
} |
297 |
|
298 |
if (anOpenAtLogin) { |
299 |
if (!appItem) |
300 |
LSSharedFileListInsertItemURL(loginItems, kLSSharedFileListItemBeforeFirst, NULL, NULL, (__bridge CFURLRef)appURL, NULL, NULL); |
301 |
openAtLogin = YES; |
302 |
} else { |
303 |
if (appItem) |
304 |
LSSharedFileListItemRemove(loginItems, appItem); |
305 |
openAtLogin = NO; |
306 |
} |
307 |
} |
308 |
} |
309 |
|
310 |
#pragma mark - |
311 |
#pragma mark NSMenuDelegate |
312 |
|
313 |
- (void)menuNeedsUpdate:(NSMenu *)menu { |
314 |
NSMenuItem *menuItem; |
315 |
[menu removeAllItems]; |
316 |
if ([menu isEqualTo:accountsMenu]) { |
317 |
[menu setAutoenablesItems:NO]; |
318 |
for (PithosAccount *pithosAccount in pithosAccounts) { |
319 |
menuItem = [[NSMenuItem alloc] initWithTitle:pithosAccount.name |
320 |
action:@selector(menuChangePithosAccount:) |
321 |
keyEquivalent:@""]; |
322 |
[menuItem setRepresentedObject:pithosAccount]; |
323 |
[menuItem setEnabled:pithosAccount.active]; |
324 |
[menuItem setState:((pithosAccount.active && [currentPithosAccount isEqualTo:pithosAccount]) ? NSOnState : NSOffState)]; |
325 |
[menu addItem:menuItem]; |
326 |
} |
327 |
} else if ([menu isEqualTo:lastSyncMenu]) { |
328 |
NSString *menuItemTitle; |
329 |
[menu setAutoenablesItems:NO]; |
330 |
for (PithosAccount *pithosAccount in pithosAccounts) { |
331 |
menuItemTitle = [NSString stringWithFormat:@"%@: %@", |
332 |
pithosAccount.name, |
333 |
[[[LastCompletedSyncTransformer alloc] init] transformedValue:pithosAccount.syncLastCompleted]]; |
334 |
if ([pithosAccount isEqualTo:syncPithosAccount] && [pithosAccount.syncDaemon isSyncing]) |
335 |
menuItemTitle = [menuItemTitle stringByAppendingString:@" (syncing)"]; |
336 |
// menuItem = [[[NSMenuItem alloc] initWithTitle:menuItemTitle |
337 |
// action:@selector(menuChangeSyncActive:) |
338 |
// keyEquivalent:@""] autorelease]; |
339 |
// [menuItem setRepresentedObject:pithosAccount]; |
340 |
// [menuItem setEnabled:pithosAccount.active]; |
341 |
// [menuItem setState:((pithosAccount.active && pithosAccount.syncActive) ? NSOnState : NSOffState)]; |
342 |
menuItem = [[NSMenuItem alloc] initWithTitle:menuItemTitle action:nil keyEquivalent:@""]; |
343 |
[menuItem setEnabled:NO]; |
344 |
[menuItem setState:NO]; |
345 |
[menu addItem:menuItem]; |
346 |
} |
347 |
[menu addItem:[NSMenuItem separatorItem]]; |
348 |
[menu addItem:[[NSMenuItem alloc] initWithTitle:@"Next Sync" |
349 |
action:@selector(sync) |
350 |
keyEquivalent:@""]]; |
351 |
} |
352 |
} |
353 |
|
354 |
#pragma mark - |
355 |
#pragma mark Actions |
356 |
|
357 |
- (IBAction)showPithosBrowser:(id)sender { |
358 |
if (!activated) |
359 |
return; |
360 |
[self.pithosBrowserController showWindow:sender]; |
361 |
[[self.pithosBrowserController window] makeKeyAndOrderFront:sender]; |
362 |
[NSApp activateIgnoringOtherApps:YES]; |
363 |
} |
364 |
|
365 |
- (IBAction)showPithosPreferences:(id)sender { |
366 |
[self.pithosPreferencesController showWindow:sender]; |
367 |
[[self.pithosPreferencesController window] makeKeyAndOrderFront:sender]; |
368 |
[NSApp activateIgnoringOtherApps:YES]; |
369 |
} |
370 |
|
371 |
- (IBAction)showPithosAbout:(id)sender { |
372 |
[NSApp orderFrontStandardAboutPanel:sender]; |
373 |
[NSApp activateIgnoringOtherApps:YES]; |
374 |
} |
375 |
|
376 |
- (void)sync { |
377 |
if (!activated || ![pithosAccounts count]) |
378 |
return; |
379 |
NSUInteger syncIndex; |
380 |
BOOL syncPithosAccountFound = [pithosAccounts containsObject:syncPithosAccount]; |
381 |
if (syncPithosAccountFound) |
382 |
syncIndex = [pithosAccounts indexOfObject:syncPithosAccount]; |
383 |
|
384 |
PithosAccount *singleSyncPithosAccount = nil; |
385 |
for (PithosAccount *pithosAccount in pithosAccounts) { |
386 |
if (!singleSyncPithosAccount && pithosAccount.active && pithosAccount.syncActive && pithosAccount.syncDaemon) { |
387 |
singleSyncPithosAccount = pithosAccount; |
388 |
} else if (singleSyncPithosAccount && pithosAccount.active && pithosAccount.syncActive && pithosAccount.syncDaemon) { |
389 |
singleSyncPithosAccount = nil; |
390 |
break; |
391 |
} |
392 |
} |
393 |
|
394 |
if (syncPithosAccount && syncPithosAccount.active && syncPithosAccount.syncActive && syncPithosAccount.syncDaemon) { |
395 |
// An active syncDaemon was previously syncing |
396 |
if (singleSyncPithosAccount && [singleSyncPithosAccount isEqualTo:syncPithosAccount]) { |
397 |
// It's the only one, sync again |
398 |
[syncPithosAccount.syncDaemon startDaemon]; |
399 |
[syncPithosAccount.syncDaemon sync]; |
400 |
return; |
401 |
} else if ([syncPithosAccount.syncDaemon isSyncing]) { |
402 |
// It's still syncing, mark it as late and return |
403 |
[syncPithosAccount.syncDaemon syncLate]; |
404 |
return; |
405 |
} |
406 |
} |
407 |
PithosAccount *newSyncPithosAccount = nil; |
408 |
if (syncPithosAccountFound) { |
409 |
for (PithosAccount *pithosAccount in [pithosAccounts objectsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(syncIndex + 1, [pithosAccounts count] - syncIndex - 1)]]) { |
410 |
if (pithosAccount.active && pithosAccount.syncActive && pithosAccount.syncDaemon) { |
411 |
newSyncPithosAccount = pithosAccount; |
412 |
break; |
413 |
} |
414 |
} |
415 |
if (!newSyncPithosAccount) { |
416 |
for (PithosAccount *pithosAccount in [pithosAccounts objectsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, syncIndex)]]) { |
417 |
if (pithosAccount.active && pithosAccount.syncActive && pithosAccount.syncDaemon) { |
418 |
newSyncPithosAccount = pithosAccount; |
419 |
break; |
420 |
} |
421 |
} |
422 |
} |
423 |
} else { |
424 |
for (PithosAccount *pithosAccount in pithosAccounts) { |
425 |
if (pithosAccount.active && pithosAccount.syncActive && pithosAccount.syncDaemon) { |
426 |
newSyncPithosAccount = pithosAccount; |
427 |
break; |
428 |
} |
429 |
} |
430 |
} |
431 |
if (newSyncPithosAccount) { |
432 |
// A different syncDaemon is found, sync it |
433 |
self.syncPithosAccount = newSyncPithosAccount; |
434 |
[syncPithosAccount.syncDaemon startDaemon]; |
435 |
[syncPithosAccount.syncDaemon sync]; |
436 |
} else if (syncPithosAccountFound && syncPithosAccount && syncPithosAccount.active && syncPithosAccount.syncActive && syncPithosAccount.syncDaemon) { |
437 |
[syncPithosAccount.syncDaemon startDaemon]; |
438 |
[syncPithosAccount.syncDaemon sync]; |
439 |
} else { |
440 |
self.syncPithosAccount = nil; |
441 |
} |
442 |
} |
443 |
|
444 |
- (void)savePithosAccounts:(id)sender { |
445 |
[userDefaults setObject:[NSKeyedArchiver archivedDataWithRootObject:pithosAccounts] forKey:@"pithosAccounts"]; |
446 |
[userDefaults synchronize]; |
447 |
} |
448 |
|
449 |
- (void)removedPithosAccount:(PithosAccount *)removedPithosAccount { |
450 |
if ([self.currentPithosAccount isEqualTo:removedPithosAccount]) { |
451 |
for (PithosAccount *pithosAccount in pithosAccounts) { |
452 |
if (pithosAccount.active) { |
453 |
self.currentPithosAccount = pithosAccount; |
454 |
self.pithosBrowserController.pithosAccountManager = currentPithosAccount; |
455 |
break; |
456 |
} |
457 |
} |
458 |
if ([self.currentPithosAccount isEqualTo:removedPithosAccount]) { |
459 |
self.activated = NO; |
460 |
[self.pithosBrowserController.window close]; |
461 |
[self.pithosBrowserController resetBrowser]; |
462 |
self.currentPithosAccount = [pithosAccounts objectAtIndex:0]; |
463 |
self.pithosBrowserController.pithosAccountManager = currentPithosAccount; |
464 |
} |
465 |
} |
466 |
if ([self.syncPithosAccount isEqualTo:removedPithosAccount]) |
467 |
self.syncPithosAccount = nil; |
468 |
} |
469 |
|
470 |
- (void)checkForUpdates { |
471 |
@synchronized(self) { |
472 |
if (!checkForUpdatesNotRunning) |
473 |
return; |
474 |
self.checkForUpdatesNotRunning = NO; |
475 |
} |
476 |
ASIHTTPRequest *checkForUpdatesRequest = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"https://code.grnet.gr/projects/pithos-macos/repository/revisions/master/raw/pithos-macos/pithos-macos-Info.plist"]]; |
477 |
checkForUpdatesRequest.delegate = self; |
478 |
checkForUpdatesRequest.didFinishSelector = @selector(checkForUpdatesRequestFinished:); |
479 |
checkForUpdatesRequest.didFailSelector = @selector(checkForUpdatesRequestFailed:); |
480 |
checkForUpdatesRequest.timeOutSeconds = 60; |
481 |
checkForUpdatesRequest.numberOfTimesToRetryOnTimeout = 10; |
482 |
[checkForUpdatesRequest startAsynchronous]; |
483 |
} |
484 |
|
485 |
#pragma mark - |
486 |
#pragma mark ASIHTTPRequestDelegate |
487 |
|
488 |
- (void)checkForUpdatesRequestFinished:(ASIHTTPRequest *)request { |
489 |
if (request.responseStatusCode == 200) { |
490 |
NSError *error = nil; |
491 |
NSDictionary *plistDictionary = [NSPropertyListSerialization propertyListWithData:[request.responseString dataUsingEncoding:NSUTF8StringEncoding] |
492 |
options:NSPropertyListImmutable |
493 |
format:NULL |
494 |
error:&error]; |
495 |
if (!error) { |
496 |
NSString *currentVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]; |
497 |
NSString *newVersion = [plistDictionary objectForKey:@"CFBundleVersion"]; |
498 |
NSURL *distributionURL = [NSURL URLWithString:[plistDictionary objectForKey:@"PithosDistributionURL"]]; |
499 |
if (newVersion && currentVersion && distributionURL && ([newVersion doubleValue] > [currentVersion doubleValue])) { |
500 |
NSAlert *alert = [[NSAlert alloc] init]; |
501 |
[alert setMessageText:@"Updates Available"]; |
502 |
[alert setInformativeText:@"An updated version is available"]; |
503 |
[alert addButtonWithTitle:@"Download"]; |
504 |
[alert runModal]; |
505 |
[[NSWorkspace sharedWorkspace] openURL:distributionURL]; |
506 |
} else if (checkForUpdatesCalledFromMenu) { |
507 |
NSAlert *alert = [[NSAlert alloc] init]; |
508 |
[alert setMessageText:@"No Updates Available"]; |
509 |
[alert setInformativeText:@"You are running the latest version"]; |
510 |
[alert addButtonWithTitle:@"OK"]; |
511 |
[alert runModal]; |
512 |
checkForUpdatesCalledFromMenu = NO; |
513 |
} |
514 |
[NSTimer scheduledTimerWithTimeInterval:86400 |
515 |
target:self |
516 |
selector:@selector(checkForUpdates) |
517 |
userInfo:nil |
518 |
repeats:YES]; |
519 |
self.checkForUpdatesNotRunning = YES; |
520 |
return; |
521 |
} |
522 |
DLog(@"Check for update plist error: %@", error); |
523 |
} |
524 |
} |
525 |
|
526 |
- (void)checkForUpdatesRequestFailed:(ASIHTTPRequest *)request { |
527 |
if (checkForUpdatesCalledFromMenu) { |
528 |
NSAlert *alert = [[NSAlert alloc] init]; |
529 |
[alert setMessageText:@"Check for Updates Error"]; |
530 |
[alert setInformativeText:@"Cannot check for updates now, try again later"]; |
531 |
[alert addButtonWithTitle:@"OK"]; |
532 |
[alert runModal]; |
533 |
checkForUpdatesCalledFromMenu = NO; |
534 |
} |
535 |
[NSTimer scheduledTimerWithTimeInterval:600 |
536 |
target:self |
537 |
selector:@selector(checkForUpdates) |
538 |
userInfo:nil |
539 |
repeats:YES]; |
540 |
self.checkForUpdatesNotRunning = YES; |
541 |
} |
542 |
|
543 |
#pragma mark - |
544 |
#pragma mark Menu Actions |
545 |
|
546 |
- (void)menuChangePithosAccount:(NSMenuItem *)sender { |
547 |
PithosAccount *pithosAccount = (PithosAccount *)[sender representedObject]; |
548 |
if (!pithosAccount.active) |
549 |
return; |
550 |
if (![currentPithosAccount isEqualTo:pithosAccount] && [pithosAccounts containsObject:pithosAccount]) { |
551 |
if ([self.pithosBrowserController operationsPending]) { |
552 |
NSAlert *alert = [[NSAlert alloc] init]; |
553 |
[alert setMessageText:@"Pending Operations"]; |
554 |
[alert setInformativeText:@"There are pending operations in the browser, do you want to change accounts and cancel them?"]; |
555 |
[alert addButtonWithTitle:@"OK"]; |
556 |
[alert addButtonWithTitle:@"Cancel"]; |
557 |
NSInteger choice = [alert runModal]; |
558 |
if (choice == NSAlertSecondButtonReturn) |
559 |
return; |
560 |
} |
561 |
self.currentPithosAccount = pithosAccount; |
562 |
[self showPithosBrowser:self]; |
563 |
self.pithosBrowserController.pithosAccountManager = currentPithosAccount; |
564 |
} |
565 |
} |
566 |
|
567 |
//- (void)menuChangeSyncActive:(NSMenuItem *)sender { |
568 |
// PithosAccount *pithosAccount = (PithosAccount *)[sender representedObject]; |
569 |
// if (!pithosAccount.active) |
570 |
// return; |
571 |
// pithosAccount.syncActive = !pithosAccount.syncActive; |
572 |
// if (self.pithosPreferencesController && [self.pithosPreferencesController.selectedPithosAccount isEqualTo:pithosAccount]) |
573 |
// self.pithosPreferencesController.syncActive = pithosAccount.syncActive; |
574 |
// [self savePithosAccounts:self]; |
575 |
//} |
576 |
|
577 |
- (IBAction)menuCheckForUpdates:(NSMenuItem *)sender { |
578 |
@synchronized(self) { |
579 |
checkForUpdatesCalledFromMenu = YES; |
580 |
} |
581 |
[self checkForUpdates]; |
582 |
} |
583 |
|
584 |
@end |