From 6422084fa0bdac8a668e39a9f81ba1e007c8d908 Mon Sep 17 00:00:00 2001 From: Maarten Billemont Date: Tue, 24 Jun 2014 20:30:15 -0400 Subject: [PATCH] UI improvements to the Mac app. --- External/Pearl | 2 +- MasterPassword/ObjC/Mac/MPMacAppDelegate.m | 454 +++++++++--------- .../ObjC/Mac/MPPasswordWindowController.m | 8 + .../ObjC/Mac/MPPasswordWindowController.xib | 39 +- .../project.pbxproj | 8 + 5 files changed, 269 insertions(+), 242 deletions(-) diff --git a/External/Pearl b/External/Pearl index e50cad14..a3f33c5f 160000 --- a/External/Pearl +++ b/External/Pearl @@ -1 +1 @@ -Subproject commit e50cad1411aaafe8083b3863e48b53a1f722c239 +Subproject commit a3f33c5f0cbafd6910ae55fb313764c18b48ad69 diff --git a/MasterPassword/ObjC/Mac/MPMacAppDelegate.m b/MasterPassword/ObjC/Mac/MPMacAppDelegate.m index e7a194f0..4b2ed8a3 100644 --- a/MasterPassword/ObjC/Mac/MPMacAppDelegate.m +++ b/MasterPassword/ObjC/Mac/MPMacAppDelegate.m @@ -26,7 +26,7 @@ @property(nonatomic, strong) NSWindowController *initialWindow; @end -@implementation MPMacAppDelegate { NSWindow *_window; } +@implementation MPMacAppDelegate #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wfour-char-constants" @@ -66,71 +66,157 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven return eventNotHandledErr; } -- (void)updateUsers { +#pragma mark - Life - [[[self.usersItem submenu] itemArray] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { - if (idx > 2) - [[self.usersItem submenu] removeItem:obj]; +- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { + + // Setup delegates and listeners. + [MPConfig get].delegate = self; + __weak id weakSelf = self; + [self addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) { + dispatch_async( dispatch_get_main_queue(), ^{ + [weakSelf updateMenuItems]; + } ); + } forKeyPath:@"key" options:0 context:nil]; + [self addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) { + dispatch_async( dispatch_get_main_queue(), ^{ + [weakSelf updateMenuItems]; + } ); + } forKeyPath:@"activeUser" options:0 context:nil]; + [self addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) { + dispatch_async( dispatch_get_main_queue(), ^{ + [weakSelf updateMenuItems]; + } ); + } forKeyPath:@"storeManager.cloudAvailable" options:0 context:nil]; + + // Status item. + self.statusView = [[RHStatusItemView alloc] initWithStatusBarItem: + [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength]]; + self.statusView.image = [NSImage imageNamed:@"menu-icon"]; + self.statusView.menu = self.statusMenu; + self.statusView.target = self; + self.statusView.action = @selector( showMenu ); + + [[NSNotificationCenter defaultCenter] addObserverForName:USMStoreDidChangeNotification object:nil + queue:[NSOperationQueue mainQueue] usingBlock: + ^(NSNotification *note) { + [self updateUsers]; }]; + [[NSNotificationCenter defaultCenter] addObserverForName:USMStoreDidImportChangesNotification object:nil + queue:[NSOperationQueue mainQueue] usingBlock: + ^(NSNotification *note) { + [self updateUsers]; + }]; + [[NSNotificationCenter defaultCenter] addObserverForName:MPCheckConfigNotification object:nil + queue:[NSOperationQueue mainQueue] usingBlock: + ^(NSNotification *note) { + NSString *key = note.object; + if (!key || [key isEqualToString:NSStringFromSelector( @selector( rememberLogin ) )]) + self.rememberPasswordItem.state = [[MPConfig get].rememberLogin boolValue]? NSOnState: NSOffState; + if (!key || [key isEqualToString:NSStringFromSelector( @selector( dialogStyleHUD ) )]) { + self.dialogStyleRegular.state = ![[MPMacConfig get].dialogStyleHUD boolValue]? NSOnState: NSOffState; + self.dialogStyleHUD.state = [[MPMacConfig get].dialogStyleHUD boolValue]? NSOnState: NSOffState; + if (![self.passwordWindow.window isVisible]) + self.passwordWindow = nil; + else { + [self.passwordWindow close]; + self.passwordWindow = nil; + [self showPasswordWindow:nil]; + } + } + }]; + [self updateUsers]; + + // Global hotkey. + EventHotKeyRef hotKeyRef; + EventTypeSpec hotKeyEvents[1] = { { .eventClass = kEventClassKeyboard, .eventKind = kEventHotKeyPressed } }; + OSStatus status = InstallApplicationEventHandler( NewEventHandlerUPP( MPHotKeyHander ), GetEventTypeCount( hotKeyEvents ), + hotKeyEvents, (__bridge void *)self, NULL ); + if (status != noErr) + err( @"Error installing application event handler: %i", (int)status ); + status = RegisterEventHotKey( 35 /* p */, controlKey + cmdKey, MPShowHotKey, GetApplicationEventTarget(), 0, &hotKeyRef ); + if (status != noErr) + err( @"Error registering 'show' hotkey: %i", (int)status ); + status = RegisterEventHotKey( 35 /* p */, controlKey + optionKey + cmdKey, MPLockHotKey, GetApplicationEventTarget(), 0, &hotKeyRef ); + if (status != noErr) + err( @"Error registering 'lock' hotkey: %i", (int)status ); + + // Initial display. + [NSApp activateIgnoringOtherApps:YES]; + if ([[MPMacConfig get].firstRun boolValue]) { + self.initialWindow = [[NSWindowController alloc] initWithWindowNibName:@"MPInitialWindow" owner:self]; + [self.initialWindow.window setLevel:NSFloatingWindowLevel]; + [self.initialWindow showWindow:self]; + } +} + +- (void)applicationWillResignActive:(NSNotification *)notification { + + if (![[MPConfig get].rememberLogin boolValue]) + [self lock:nil]; +} + +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender { + // Save changes in the application's managed object context before the application terminates. NSManagedObjectContext *context = [MPMacAppDelegate managedObjectContextForMainThreadIfReady]; - if (!context) { - self.createUserItem.title = @"New User (Not ready)"; - self.createUserItem.enabled = NO; - self.createUserItem.toolTip = @"Please wait until the app is fully loaded."; - self.deleteUserItem.title = @"Delete User (Not ready)"; - self.deleteUserItem.enabled = NO; - self.deleteUserItem.toolTip = @"Please wait until the app is fully loaded."; - [self.usersItem.submenu addItemWithTitle:@"Loading..." action:NULL keyEquivalent:@""].enabled = NO; + if (!context) + return NSTerminateNow; - return; - } + if (![context commitEditing]) + return NSTerminateCancel; - MPUserEntity *activeUser = [self activeUserInContext:context]; + if (![context hasChanges]) + return NSTerminateNow; - self.createUserItem.title = @"New User"; - self.createUserItem.enabled = YES; - self.createUserItem.toolTip = nil; - - self.deleteUserItem.title = activeUser? @"Delete User": @"Delete User (None Selected)"; - self.deleteUserItem.enabled = activeUser != nil; - self.deleteUserItem.toolTip = activeUser? nil: @"First select the user to delete."; - - NSError *error = nil; - NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )]; - fetchRequest.sortDescriptors = @[ [NSSortDescriptor sortDescriptorWithKey:@"lastUsed" ascending:NO] ]; - NSArray *users = [context executeFetchRequest:fetchRequest error:&error]; - if (!users) - err( @"Failed to load users: %@", error ); - - if (![users count]) { - NSMenuItem *noUsersItem = [self.usersItem.submenu addItemWithTitle:@"No users" action:NULL keyEquivalent:@""]; - noUsersItem.enabled = NO; - noUsersItem.toolTip = @"Use the iOS app to create users and make sure iCloud is enabled in its preferences as well. " - @"Then give iCloud some time to sync the new user to your Mac."; - } - - self.usersItem.state = NSMixedState; - for (MPUserEntity *user in users) { - NSMenuItem *userItem = [[NSMenuItem alloc] initWithTitle:user.name action:@selector( selectUser: ) keyEquivalent:@""]; - [userItem setTarget:self]; - [userItem setRepresentedObject:[user objectID]]; - [[self.usersItem submenu] addItem:userItem]; - - if (!activeUser && [user.name isEqualToString:[MPMacConfig get].usedUserName]) - [super setActiveUser:activeUser = user]; - - if ([activeUser isEqual:user]) { - userItem.state = NSOnState; - self.usersItem.state = NSOffState; - } - else - userItem.state = NSOffState; - } - - [self updateMenuItems]; + [context saveToStore]; + return NSTerminateNow; } +#pragma mark - State + +- (void)setActiveUser:(MPUserEntity *)activeUser { + + [super setActiveUser:activeUser]; + + [MPMacConfig get].usedUserName = activeUser.name; + + PearlMainQueue( ^{ + [self updateUsers]; + } ); +} + +- (void)setLoginItemEnabled:(BOOL)enabled { + + BOOL loginItemEnabled = [self loginItemEnabled]; + if (loginItemEnabled != enabled) { + if (SMLoginItemSetEnabled( (__bridge CFStringRef)LOGIN_HELPER_BUNDLE_ID, (Boolean)enabled ) == true) + loginItemEnabled = enabled; + else + wrn( @"Failed to set login item." ); + } + + self.openAtLoginItem.state = loginItemEnabled? NSOnState: NSOffState; + self.openAtLoginButton.state = loginItemEnabled? NSOnState: NSOffState; +} + +- (BOOL)loginItemEnabled { + + // The easy and sane method (SMJobCopyDictionary) can pose problems when the app is sandboxed. -_- + NSArray *jobs = (__bridge_transfer NSArray *)SMCopyAllJobDictionaries( kSMDomainUserLaunchd ); + + for (NSDictionary *job in jobs) + if ([LOGIN_HELPER_BUNDLE_ID isEqualToString:[job objectForKey:@"Label"]]) { + dbg( @"loginItemEnabled: %@", @([[job objectForKey:@"OnDemand"] boolValue]) ); + return [[job objectForKey:@"OnDemand"] boolValue]; + } + + dbg( @"loginItemEnabled: not found" ); + return NO; +} + +#pragma mark - Actions + - (void)selectUser:(NSMenuItem *)item { [self signOutAnimated:NO]; @@ -143,13 +229,6 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven err( @"While looking up selected user: %@", error ); } -- (void)showMenu { - - [self updateMenuItems]; - - [self.statusView popUpMenu]; -} - - (IBAction)togglePreference:(id)sender { if (sender == self.enableCloudButton) { @@ -278,104 +357,99 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven self.initialWindow = nil; } -- (void)didUpdateConfigForKey:(SEL)configKey fromValue:(id)oldValue { +- (IBAction)showPasswordWindow:(id)sender { - [[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:NSStringFromSelector( configKey )]; -} - -#pragma mark - NSApplicationDelegate - -- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { - - // Setup delegates and listeners. - [MPConfig get].delegate = self; - __weak id weakSelf = self; - [self addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) { - dispatch_async( dispatch_get_main_queue(), ^{ - [weakSelf updateMenuItems]; - } ); - } forKeyPath:@"key" options:0 context:nil]; - [self addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) { - dispatch_async( dispatch_get_main_queue(), ^{ - [weakSelf updateMenuItems]; - } ); - } forKeyPath:@"activeUser" options:0 context:nil]; - [self addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) { - dispatch_async( dispatch_get_main_queue(), ^{ - [weakSelf updateMenuItems]; - } ); - } forKeyPath:@"storeManager.cloudAvailable" options:0 context:nil]; - - // Status item. - self.statusView = [[RHStatusItemView alloc] initWithStatusBarItem: - [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength]]; - self.statusView.image = [NSImage imageNamed:@"menu-icon"]; - self.statusView.menu = self.statusMenu; - self.statusView.target = self; - self.statusView.action = @selector( showMenu ); - - [[NSNotificationCenter defaultCenter] addObserverForName:USMStoreDidChangeNotification object:nil - queue:[NSOperationQueue mainQueue] usingBlock: - ^(NSNotification *note) { - [self updateUsers]; - }]; - [[NSNotificationCenter defaultCenter] addObserverForName:USMStoreDidImportChangesNotification object:nil - queue:[NSOperationQueue mainQueue] usingBlock: - ^(NSNotification *note) { - [self updateUsers]; - }]; - [[NSNotificationCenter defaultCenter] addObserverForName:MPCheckConfigNotification object:nil - queue:[NSOperationQueue mainQueue] usingBlock: - ^(NSNotification *note) { - NSString *key = note.object; - if (!key || [key isEqualToString:NSStringFromSelector( @selector( rememberLogin ) )]) - self.rememberPasswordItem.state = [[MPConfig get].rememberLogin boolValue]? NSOnState: NSOffState; - if (!key || [key isEqualToString:NSStringFromSelector( @selector( dialogStyleHUD ) )]) { - self.dialogStyleRegular.state = ![[MPMacConfig get].dialogStyleHUD boolValue]? NSOnState: NSOffState; - self.dialogStyleHUD.state = [[MPMacConfig get].dialogStyleHUD boolValue]? NSOnState: NSOffState; - if (![self.passwordWindow.window isVisible]) - self.passwordWindow = nil; - else { - [self.passwordWindow close]; - self.passwordWindow = nil; - [self showPasswordWindow:nil]; - } - } - }]; - [self updateUsers]; - - // Global hotkey. - EventHotKeyRef hotKeyRef; - EventTypeSpec hotKeyEvents[1] = { { .eventClass = kEventClassKeyboard, .eventKind = kEventHotKeyPressed } }; - OSStatus status = InstallApplicationEventHandler( NewEventHandlerUPP( MPHotKeyHander ), GetEventTypeCount( hotKeyEvents ), - hotKeyEvents, (__bridge void *)self, NULL ); - if (status != noErr) - err( @"Error installing application event handler: %i", (int)status ); - status = RegisterEventHotKey( 35 /* p */, controlKey + cmdKey, MPShowHotKey, GetApplicationEventTarget(), 0, &hotKeyRef ); - if (status != noErr) - err( @"Error registering 'show' hotkey: %i", (int)status ); - status = RegisterEventHotKey( 35 /* p */, controlKey + optionKey + cmdKey, MPLockHotKey, GetApplicationEventTarget(), 0, &hotKeyRef ); - if (status != noErr) - err( @"Error registering 'lock' hotkey: %i", (int)status ); - - // Initial display. [NSApp activateIgnoringOtherApps:YES]; - if ([[MPMacConfig get].firstRun boolValue]) { - self.initialWindow = [[NSWindowController alloc] initWithWindowNibName:@"MPInitialWindow" owner:self]; - [self.initialWindow.window setLevel:NSFloatingWindowLevel]; - [self.initialWindow showWindow:self]; + + // If no user, can't activate. + if (![self activeUserForMainThread]) { + [[NSAlert alertWithMessageText:@"No User Selected" defaultButton:[PearlStrings get].commonButtonOkay alternateButton:nil + otherButton:nil informativeTextWithFormat: + @"Begin by selecting or creating your user from the status menu (●●●|) next to the clock."] + runModal]; + [self.statusView popUpMenu]; + return; } + + // Don't show window if we weren't already running (ie. if we haven't been activated before). + if (!self.passwordWindow) + self.passwordWindow = [[MPPasswordWindowController alloc] initWithWindowNibName:@"MPPasswordWindowController"]; + + [self.passwordWindow showWindow:self]; } -- (void)setActiveUser:(MPUserEntity *)activeUser { +#pragma mark - Private - [super setActiveUser:activeUser]; +- (void)updateUsers { - [MPMacConfig get].usedUserName = activeUser.name; + [[[self.usersItem submenu] itemArray] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + if (idx > 2) + [[self.usersItem submenu] removeItem:obj]; + }]; - PearlMainQueue( ^{ - [self updateUsers]; - } ); + NSManagedObjectContext *context = [MPMacAppDelegate managedObjectContextForMainThreadIfReady]; + if (!context) { + self.createUserItem.title = @"New User (Not ready)"; + self.createUserItem.enabled = NO; + self.createUserItem.toolTip = @"Please wait until the app is fully loaded."; + self.deleteUserItem.title = @"Delete User (Not ready)"; + self.deleteUserItem.enabled = NO; + self.deleteUserItem.toolTip = @"Please wait until the app is fully loaded."; + [self.usersItem.submenu addItemWithTitle:@"Loading..." action:NULL keyEquivalent:@""].enabled = NO; + + return; + } + + MPUserEntity *activeUser = [self activeUserInContext:context]; + + self.createUserItem.title = @"New User"; + self.createUserItem.enabled = YES; + self.createUserItem.toolTip = nil; + + self.deleteUserItem.title = activeUser? @"Delete User": @"Delete User (None Selected)"; + self.deleteUserItem.enabled = activeUser != nil; + self.deleteUserItem.toolTip = activeUser? nil: @"First select the user to delete."; + + NSError *error = nil; + NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )]; + fetchRequest.sortDescriptors = @[ [NSSortDescriptor sortDescriptorWithKey:@"lastUsed" ascending:NO] ]; + NSArray *users = [context executeFetchRequest:fetchRequest error:&error]; + if (!users) + err( @"Failed to load users: %@", error ); + + if (![users count]) { + NSMenuItem *noUsersItem = [self.usersItem.submenu addItemWithTitle:@"No users" action:NULL keyEquivalent:@""]; + noUsersItem.enabled = NO; + noUsersItem.toolTip = @"Use the iOS app to create users and make sure iCloud is enabled in its preferences as well. " + @"Then give iCloud some time to sync the new user to your Mac."; + } + + self.usersItem.state = NSMixedState; + for (MPUserEntity *user in users) { + NSMenuItem *userItem = [[NSMenuItem alloc] initWithTitle:user.name action:@selector( selectUser: ) keyEquivalent:@""]; + [userItem setTarget:self]; + [userItem setRepresentedObject:[user objectID]]; + [[self.usersItem submenu] addItem:userItem]; + + if (!activeUser && [user.name isEqualToString:[MPMacConfig get].usedUserName]) + [super setActiveUser:activeUser = user]; + + if ([activeUser isEqual:user]) { + userItem.state = NSOnState; + self.usersItem.state = NSOffState; + } + else + userItem.state = NSOffState; + } + + [self updateMenuItems]; +} + +- (void)showMenu { + + [self updateMenuItems]; + + [self.statusView popUpMenu]; } - (void)updateMenuItems { @@ -440,77 +514,11 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven } } -- (IBAction)showPasswordWindow:(id)sender { +#pragma mark - PearlConfigDelegate - [NSApp activateIgnoringOtherApps:YES]; +- (void)didUpdateConfigForKey:(SEL)configKey fromValue:(id)oldValue { - // If no user, can't activate. - if (![self activeUserForMainThread]) { - [[NSAlert alertWithMessageText:@"No User Selected" defaultButton:[PearlStrings get].commonButtonOkay alternateButton:nil - otherButton:nil informativeTextWithFormat: - @"Begin by selecting or creating your user from the status menu (●●●|) next to the clock."] - runModal]; - [self.statusView popUpMenu]; - return; - } - - // Don't show window if we weren't already running (ie. if we haven't been activated before). - if (!self.passwordWindow) - self.passwordWindow = [[MPPasswordWindowController alloc] initWithWindowNibName:@"MPPasswordWindowController"]; - - [self.passwordWindow showWindow:self]; -} - -- (void)setLoginItemEnabled:(BOOL)enabled { - - BOOL loginItemEnabled = [self loginItemEnabled]; - if (loginItemEnabled != enabled) { - if (SMLoginItemSetEnabled( (__bridge CFStringRef)LOGIN_HELPER_BUNDLE_ID, (Boolean)enabled ) == true) - loginItemEnabled = enabled; - else - wrn( @"Failed to set login item." ); - } - - self.openAtLoginItem.state = loginItemEnabled? NSOnState: NSOffState; - self.openAtLoginButton.state = loginItemEnabled? NSOnState: NSOffState; -} - -- (BOOL)loginItemEnabled { - - // The easy and sane method (SMJobCopyDictionary) can pose problems when the app is sandboxed. -_- - NSArray *jobs = (__bridge_transfer NSArray *)SMCopyAllJobDictionaries( kSMDomainUserLaunchd ); - - for (NSDictionary *job in jobs) - if ([LOGIN_HELPER_BUNDLE_ID isEqualToString:[job objectForKey:@"Label"]]) { - dbg( @"loginItemEnabled: %@", @([[job objectForKey:@"OnDemand"] boolValue]) ); - return [[job objectForKey:@"OnDemand"] boolValue]; - } - - dbg( @"loginItemEnabled: not found" ); - return NO; -} - -- (void)applicationWillResignActive:(NSNotification *)notification { - - if (![[MPConfig get].rememberLogin boolValue]) - [self lock:nil]; -} - -- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender { - // Save changes in the application's managed object context before the application terminates. - - NSManagedObjectContext *context = [MPMacAppDelegate managedObjectContextForMainThreadIfReady]; - if (!context) - return NSTerminateNow; - - if (![context commitEditing]) - return NSTerminateCancel; - - if (![context hasChanges]) - return NSTerminateNow; - - [context saveToStore]; - return NSTerminateNow; + [[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:NSStringFromSelector( configKey )]; } @end diff --git a/MasterPassword/ObjC/Mac/MPPasswordWindowController.m b/MasterPassword/ObjC/Mac/MPPasswordWindowController.m index a1b69ef5..2a1d9724 100644 --- a/MasterPassword/ObjC/Mac/MPPasswordWindowController.m +++ b/MasterPassword/ObjC/Mac/MPPasswordWindowController.m @@ -22,6 +22,7 @@ #import "MPAppDelegate_Store.h" #import "MPElementModel.h" #import "MPAppDelegate_Key.h" +#import "PearlProfiler.h" #define MPAlertIncorrectMP @"MPAlertIncorrectMP" #define MPAlertCreateSite @"MPAlertCreateSite" @@ -357,12 +358,15 @@ if ([self.window isOnActiveSpace] && self.window.alphaValue) return; + PearlProfiler *profiler = [PearlProfiler new]; CGWindowID windowID = (CGWindowID)[self.window windowNumber]; CGImageRef capturedImage = CGWindowListCreateImage( CGRectInfinite, kCGWindowListOptionOnScreenBelowWindow, windowID, kCGWindowImageBoundsIgnoreFraming ); + [profiler finishJob:@"captured window: %d, on screen: %@", windowID, self.window.screen]; NSImage *screenImage = [[NSImage alloc] initWithCGImage:capturedImage size:NSMakeSize( CGImageGetWidth( capturedImage ) / self.window.backingScaleFactor, CGImageGetHeight( capturedImage ) / self.window.backingScaleFactor )]; + [profiler finishJob:@"image size: %@, bytes: %ld", NSStringFromSize( screenImage.size ), screenImage.TIFFRepresentation.length ]; NSImage *smallImage = [[NSImage alloc] initWithSize:NSMakeSize( CGImageGetWidth( capturedImage ) / 20, @@ -373,12 +377,16 @@ operation:NSCompositeSourceOver fraction:1.0]; [smallImage unlockFocus]; + [profiler finishJob:@"small image size: %@, bytes: %ld", NSStringFromSize( screenImage.size ), screenImage.TIFFRepresentation.length]; self.blurView.image = smallImage; + [profiler finishJob:@"assigned image"]; [self.window setFrame:self.window.screen.frame display:YES]; + [profiler finishJob:@"assigned frame"]; [[NSAnimationContext currentContext] setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]]; [[self.window animator] setAlphaValue:1.0]; + [profiler finishJob:@"animating window"]; } - (void)fadeOut { diff --git a/MasterPassword/ObjC/Mac/MPPasswordWindowController.xib b/MasterPassword/ObjC/Mac/MPPasswordWindowController.xib index 4ca07c0c..5fb88038 100644 --- a/MasterPassword/ObjC/Mac/MPPasswordWindowController.xib +++ b/MasterPassword/ObjC/Mac/MPPasswordWindowController.xib @@ -42,25 +42,15 @@ - + - - - - - - - - - - - + + + + +YnBsaXN0MDDUAQIDBAUGICFYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoKcHCA8Q +CRUbVSRudWxs0wkKCwwNDlR0eXBlViRjbGFzc18QEl9fQ0FDb2RpbmdDb250ZW50c4ACgAaAA1RmYWRl +0hEKEhRaTlMub2JqZWN0c6ETgASABdIWFxgZWiRjbGFzc25hbWVYJGNsYXNzZXNXTlNBcnJheaIYGlhO +U09iamVjdNIWFxwdXENBVHJhbnNpdGlvbqMeHxpcQ0FUcmFuc2l0aW9uW0NBQW5pbWF0aW9uXxAPTlNL +ZXllZEFyY2hpdmVy0SIjVHJvb3SAAQAIABEAGgAjAC0AMgA3AD8ARQBMAFEAWABtAG8AcQBzAHgAfQCI +AIoAjACOAJMAngCnAK8AsgC7AMAAzQDRAN4A6gD8AP8BBAAAAAAAAAIBAAAAAAAAACQAAAAAAAAAAAAA +AAAAAAEGA + + + diff --git a/MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/project.pbxproj b/MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/project.pbxproj index 02b6b7a5..4c4a46d8 100644 --- a/MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/project.pbxproj +++ b/MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/project.pbxproj @@ -10,9 +10,11 @@ 93D390C676DF52DA7E459F19 /* MPPasswordWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39D9D0061FF1159998F06 /* MPPasswordWindow.m */; }; 93D392EC39DA43C46C692C12 /* NSDictionary+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */; }; 93D395F08A087F8A24689347 /* NSArray+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */; }; + 93D3970BCF85F7902E611168 /* PearlProfiler.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39DB3A8ADED08C39A6228 /* PearlProfiler.m */; }; 93D39C34FE35830EF5BE1D2A /* NSArray+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D396D04E57792A54D437AC /* NSArray+Indexing.h */; }; 93D39C5789EFA607CF788082 /* MPElementModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39E73BF5CBF8E5B005CD3 /* MPElementModel.m */; }; 93D39C7C2BE7C0E0763B0177 /* MPElementCollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D394495528B10D1B61A2C3 /* MPElementCollectionView.m */; }; + 93D39D304F73B3BBA031522A /* PearlProfiler.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D394EEFF5BF555A55AF361 /* PearlProfiler.h */; }; 93D39E281E3658B30550CB55 /* NSDictionary+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */; }; 93D39F833DEC1C89B2F795AC /* MPPasswordWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39A57A7823DE98A0FF83C /* MPPasswordWindowController.m */; }; DA0933CC1747AD2D00DE1CEF /* shot-laptop-leaning-iphone.png in Resources */ = {isa = PBXBuildFile; fileRef = DA0933CB1747AD2D00DE1CEF /* shot-laptop-leaning-iphone.png */; }; @@ -309,12 +311,14 @@ 93D392C3918763B3B72CF366 /* MPPasswordWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordWindowController.h; sourceTree = ""; }; 93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Indexing.h"; sourceTree = ""; }; 93D394495528B10D1B61A2C3 /* MPElementCollectionView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementCollectionView.m; sourceTree = ""; }; + 93D394EEFF5BF555A55AF361 /* PearlProfiler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PearlProfiler.h; path = ../../../External/Pearl/Pearl/PearlProfiler.h; sourceTree = ""; }; 93D3960D320FF8A072B092E3 /* MPElementCollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementCollectionView.h; sourceTree = ""; }; 93D396D04E57792A54D437AC /* NSArray+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Indexing.h"; sourceTree = ""; }; 93D3977484534E99F9BA579D /* MPPasswordWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordWindow.h; sourceTree = ""; }; 93D39A57A7823DE98A0FF83C /* MPPasswordWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordWindowController.m; sourceTree = ""; }; 93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+Indexing.m"; sourceTree = ""; }; 93D39D9D0061FF1159998F06 /* MPPasswordWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordWindow.m; sourceTree = ""; }; + 93D39DB3A8ADED08C39A6228 /* PearlProfiler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PearlProfiler.m; path = ../../../External/Pearl/Pearl/PearlProfiler.m; sourceTree = ""; }; 93D39E73BF5CBF8E5B005CD3 /* MPElementModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementModel.m; sourceTree = ""; }; DA0933C91747A56A00DE1CEF /* MPInitialWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MPInitialWindow.xib; sourceTree = ""; }; DA0933CB1747AD2D00DE1CEF /* shot-laptop-leaning-iphone.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "shot-laptop-leaning-iphone.png"; sourceTree = ""; }; @@ -702,6 +706,8 @@ DACA22121705DDC5002C6C22 /* External */, DA5BFA47147E415C00F98B1E /* Frameworks */, DA5BFA45147E415C00F98B1E /* Products */, + 93D39DB3A8ADED08C39A6228 /* PearlProfiler.m */, + 93D394EEFF5BF555A55AF361 /* PearlProfiler.h */, ); sourceTree = ""; }; @@ -1343,6 +1349,7 @@ DAEB93E718AB0FFD000490CC /* crypto.h in Headers */, DAEB941318AB0FFD000490CC /* ssl2.h in Headers */, DAEB940D18AB0FFD000490CC /* ripemd.h in Headers */, + 93D39D304F73B3BBA031522A /* PearlProfiler.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1715,6 +1722,7 @@ 93D395F08A087F8A24689347 /* NSArray+Indexing.m in Sources */, 93D39E281E3658B30550CB55 /* NSDictionary+Indexing.m in Sources */, DA3509FF15F101A500C14A8E /* PearlQueue.m in Sources */, + 93D3970BCF85F7902E611168 /* PearlProfiler.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; };