From c5fc87b7b5e6669de5c806f410cc04ed0945b2f2 Mon Sep 17 00:00:00 2001 From: Maarten Billemont Date: Sat, 21 Jun 2014 21:56:28 -0400 Subject: [PATCH] An experimental new mac ui that blurs the whole screen. --- .../ObjC/Mac/MPElementCollectionView.m | 3 +- MasterPassword/ObjC/Mac/MPMacAppDelegate.h | 2 +- MasterPassword/ObjC/Mac/MPMacAppDelegate.m | 57 +- .../ObjC/Mac/MPPasswordDialogController.h | 23 + .../ObjC/Mac/MPPasswordDialogController.m | 477 ++++++++++++++++ .../ObjC/Mac/MPPasswordDialogController.xib | 380 +++++++++++++ MasterPassword/ObjC/Mac/MPPasswordWindow.h | 23 + MasterPassword/ObjC/Mac/MPPasswordWindow.m | 43 ++ .../ObjC/Mac/MPPasswordWindowController.h | 26 +- .../ObjC/Mac/MPPasswordWindowController.m | 516 +++-------------- .../ObjC/Mac/MPPasswordWindowController.xib | 523 ++++++------------ .../project.pbxproj | 134 ++++- 12 files changed, 1364 insertions(+), 843 deletions(-) create mode 100644 MasterPassword/ObjC/Mac/MPPasswordDialogController.h create mode 100644 MasterPassword/ObjC/Mac/MPPasswordDialogController.m create mode 100644 MasterPassword/ObjC/Mac/MPPasswordDialogController.xib create mode 100644 MasterPassword/ObjC/Mac/MPPasswordWindow.h create mode 100644 MasterPassword/ObjC/Mac/MPPasswordWindow.m diff --git a/MasterPassword/ObjC/Mac/MPElementCollectionView.m b/MasterPassword/ObjC/Mac/MPElementCollectionView.m index 498e71f8..401adacf 100644 --- a/MasterPassword/ObjC/Mac/MPElementCollectionView.m +++ b/MasterPassword/ObjC/Mac/MPElementCollectionView.m @@ -21,6 +21,7 @@ #import "MPElementModel.h" #import "MPMacAppDelegate.h" #import "MPAppDelegate_Store.h" +#import "MPPasswordDialogController.h" #define MPAlertChangeType @"MPAlertChangeType" #define MPAlertChangeLogin @"MPAlertChangeLogin" @@ -204,7 +205,7 @@ [context deleteObject:element]; [context saveToStore]; - [((MPPasswordWindowController *)self.collectionView.window.windowController) updateElements]; + [((MPPasswordDialogController *)self.collectionView.window.windowController) updateElements]; }]; break; } diff --git a/MasterPassword/ObjC/Mac/MPMacAppDelegate.h b/MasterPassword/ObjC/Mac/MPMacAppDelegate.h index 320865d7..356ca159 100644 --- a/MasterPassword/ObjC/Mac/MPMacAppDelegate.h +++ b/MasterPassword/ObjC/Mac/MPMacAppDelegate.h @@ -8,8 +8,8 @@ #import #import "MPAppDelegate_Shared.h" -#import "MPPasswordWindowController.h" #import "RHStatusItemView.h" +#import "MPPasswordWindowController.h" @interface MPMacAppDelegate : MPAppDelegate_Shared diff --git a/MasterPassword/ObjC/Mac/MPMacAppDelegate.m b/MasterPassword/ObjC/Mac/MPMacAppDelegate.m index 2f9d682d..e7a194f0 100644 --- a/MasterPassword/ObjC/Mac/MPMacAppDelegate.m +++ b/MasterPassword/ObjC/Mac/MPMacAppDelegate.m @@ -9,12 +9,13 @@ #import "MPMacAppDelegate.h" #import "MPAppDelegate_Key.h" #import "MPAppDelegate_Store.h" +#import "MPPasswordWindowController.h" #import #import #define LOGIN_HELPER_BUNDLE_ID @"com.lyndir.lhunath.MasterPassword.Mac.LoginHelper" -@interface UbiquityStoreManager (Private) +@interface UbiquityStoreManager(Private) - (void)markCloudStoreCorrupted; @@ -25,7 +26,7 @@ @property(nonatomic, strong) NSWindowController *initialWindow; @end -@implementation MPMacAppDelegate +@implementation MPMacAppDelegate { NSWindow *_window; } #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wfour-char-constants" @@ -50,7 +51,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven // Extract the hotkey ID. EventHotKeyID hotKeyID; GetEventParameter( theEvent, kEventParamDirectObject, typeEventHotKeyID, - NULL, sizeof(hotKeyID), NULL, &hotKeyID ); + NULL, sizeof( hotKeyID ), NULL, &hotKeyID ); // Check which hotkey this was. if (hotKeyID.signature == MPShowHotKey.signature && hotKeyID.id == MPShowHotKey.id) { @@ -100,7 +101,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven fetchRequest.sortDescriptors = @[ [NSSortDescriptor sortDescriptorWithKey:@"lastUsed" ascending:NO] ]; NSArray *users = [context executeFetchRequest:fetchRequest error:&error]; if (!users) - err(@"Failed to load users: %@", error); + err( @"Failed to load users: %@", error ); if (![users count]) { NSMenuItem *noUsersItem = [self.usersItem.submenu addItemWithTitle:@"No users" action:NULL keyEquivalent:@""]; @@ -111,7 +112,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven self.usersItem.state = NSMixedState; for (MPUserEntity *user in users) { - NSMenuItem *userItem = [[NSMenuItem alloc] initWithTitle:user.name action:@selector(selectUser:) keyEquivalent:@""]; + NSMenuItem *userItem = [[NSMenuItem alloc] initWithTitle:user.name action:@selector( selectUser: ) keyEquivalent:@""]; [userItem setTarget:self]; [userItem setRepresentedObject:[user objectID]]; [[self.usersItem submenu] addItem:userItem]; @@ -139,7 +140,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven self.activeUser = (MPUserEntity *)[context existingObjectWithID:[item representedObject] error:&error]; if (error) - err(@"While looking up selected user: %@", error); + err( @"While looking up selected user: %@", error ); } - (void)showMenu { @@ -206,7 +207,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven [moc saveToStore]; NSError *error = nil; if (![moc obtainPermanentIDsForObjects:@[ newUser ] error:&error]) - err(@"Failed to obtain permanent object ID for new user: %@", error); + err( @"Failed to obtain permanent object ID for new user: %@", error ); [[NSOperationQueue mainQueue] addOperationWithBlock:^{ [self updateUsers]; @@ -290,19 +291,19 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven [MPConfig get].delegate = self; __weak id weakSelf = self; [self addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) { - dispatch_async(dispatch_get_main_queue(), ^{ + 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(), ^{ + 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(), ^{ + dispatch_async( dispatch_get_main_queue(), ^{ [weakSelf updateMenuItems]; - }); + } ); } forKeyPath:@"storeManager.cloudAvailable" options:0 context:nil]; // Status item. @@ -311,18 +312,18 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven self.statusView.image = [NSImage imageNamed:@"menu-icon"]; self.statusView.menu = self.statusMenu; self.statusView.target = self; - self.statusView.action = @selector(showMenu); + self.statusView.action = @selector( showMenu ); [[NSNotificationCenter defaultCenter] addObserverForName:USMStoreDidChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock: ^(NSNotification *note) { - [self updateUsers]; - }]; + [self updateUsers]; + }]; [[NSNotificationCenter defaultCenter] addObserverForName:USMStoreDidImportChangesNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock: ^(NSNotification *note) { - [self updateUsers]; - }]; + [self updateUsers]; + }]; [[NSNotificationCenter defaultCenter] addObserverForName:MPCheckConfigNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock: ^(NSNotification *note) { @@ -346,16 +347,16 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven // Global hotkey. EventHotKeyRef hotKeyRef; EventTypeSpec hotKeyEvents[1] = { { .eventClass = kEventClassKeyboard, .eventKind = kEventHotKeyPressed } }; - OSStatus status = InstallApplicationEventHandler(NewEventHandlerUPP( MPHotKeyHander ), GetEventTypeCount( hotKeyEvents ), - hotKeyEvents, (__bridge void *)self, NULL); + OSStatus status = InstallApplicationEventHandler( NewEventHandlerUPP( MPHotKeyHander ), GetEventTypeCount( hotKeyEvents ), + hotKeyEvents, (__bridge void *)self, NULL ); if (status != noErr) - err(@"Error installing application event handler: %i", (int)status); + 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); + 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); + err( @"Error registering 'lock' hotkey: %i", (int)status ); // Initial display. [NSApp activateIgnoringOtherApps:YES]; @@ -372,9 +373,9 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven [MPMacConfig get].usedUserName = activeUser.name; - PearlMainQueue(^{ + PearlMainQueue( ^{ [self updateUsers]; - }); + } ); } - (void)updateMenuItems { @@ -467,7 +468,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven if (SMLoginItemSetEnabled( (__bridge CFStringRef)LOGIN_HELPER_BUNDLE_ID, (Boolean)enabled ) == true) loginItemEnabled = enabled; else - wrn(@"Failed to set login item."); + wrn( @"Failed to set login item." ); } self.openAtLoginItem.state = loginItemEnabled? NSOnState: NSOffState; @@ -481,11 +482,11 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven for (NSDictionary *job in jobs) if ([LOGIN_HELPER_BUNDLE_ID isEqualToString:[job objectForKey:@"Label"]]) { - dbg(@"loginItemEnabled: %@", @([[job objectForKey:@"OnDemand"] boolValue])); + dbg( @"loginItemEnabled: %@", @([[job objectForKey:@"OnDemand"] boolValue]) ); return [[job objectForKey:@"OnDemand"] boolValue]; } - dbg(@"loginItemEnabled: not found"); + dbg( @"loginItemEnabled: not found" ); return NO; } diff --git a/MasterPassword/ObjC/Mac/MPPasswordDialogController.h b/MasterPassword/ObjC/Mac/MPPasswordDialogController.h new file mode 100644 index 00000000..78587a2e --- /dev/null +++ b/MasterPassword/ObjC/Mac/MPPasswordDialogController.h @@ -0,0 +1,23 @@ +// +// MPPasswordDialogController.h +// MasterPassword-Mac +// +// Created by Maarten Billemont on 04/03/12. +// Copyright (c) 2012 Lyndir. All rights reserved. +// + +#import +@class MPElementModel; + +@interface MPPasswordDialogController : NSWindowController + +@property(nonatomic, strong) NSMutableArray *elements; +@property(nonatomic, strong) NSIndexSet *elementSelectionIndexes; +@property(nonatomic, weak) IBOutlet NSTextField *siteField; +@property(nonatomic, weak) IBOutlet NSView *contentContainer; +@property(nonatomic, weak) IBOutlet NSTextField *userLabel; +@property(nonatomic, weak) IBOutlet NSCollectionView *siteCollectionView; + +- (void)updateElements; + +@end diff --git a/MasterPassword/ObjC/Mac/MPPasswordDialogController.m b/MasterPassword/ObjC/Mac/MPPasswordDialogController.m new file mode 100644 index 00000000..ca419579 --- /dev/null +++ b/MasterPassword/ObjC/Mac/MPPasswordDialogController.m @@ -0,0 +1,477 @@ +// +// MPPasswordDialogController.m +// MasterPassword-Mac +// +// Created by Maarten Billemont on 04/03/12. +// Copyright (c) 2012 Lyndir. All rights reserved. +// + +#import "MPPasswordDialogController.h" +#import "MPMacAppDelegate.h" +#import "MPAppDelegate_Key.h" +#import "MPAppDelegate_Store.h" +#import "MPElementModel.h" + +#define MPAlertUnlockMP @"MPAlertUnlockMP" +#define MPAlertIncorrectMP @"MPAlertIncorrectMP" +#define MPAlertCreateSite @"MPAlertCreateSite" + +@interface MPPasswordDialogController() + +@property(nonatomic) BOOL inProgress; + +@property(nonatomic, strong) NSOperationQueue *backgroundQueue; +@property(nonatomic, strong) NSAlert *loadingDataAlert; +@property(nonatomic) BOOL closing; + +@end + +@implementation MPPasswordDialogController + +#pragma mark - Life + +- (void)windowDidLoad { + + if ([[MPMacConfig get].dialogStyleHUD boolValue]) { + self.window.styleMask = NSHUDWindowMask | NSTitledWindowMask | NSUtilityWindowMask | NSClosableWindowMask; + self.userLabel.textColor = [NSColor whiteColor]; + } + else { + self.window.styleMask = NSTexturedBackgroundWindowMask | NSResizableWindowMask | NSTitledWindowMask | NSClosableWindowMask; + self.userLabel.textColor = [NSColor controlTextColor]; + } + + self.backgroundQueue = [NSOperationQueue new]; + self.backgroundQueue.maxConcurrentOperationCount = 1; + + [[MPMacAppDelegate get] addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) { +// [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) { +// if (![MPAlgorithmDefault migrateUser:[[MPMacAppDelegate get] activeUserInContext:moc]]) +// [NSAlert alertWithMessageText:@"Migration Needed" defaultButton:@"OK" alternateButton:nil otherButton:nil +// informativeTextWithFormat:@"Certain sites require explicit migration to get updated to the latest version of the " +// @"Master Password algorithm. For these sites, a migration button will appear. Migrating these sites will cause " +// @"their passwords to change. You'll need to update your profile for that site with the new password."]; +// [moc saveToStore]; +// }]; + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [self ensureLoadedAndUnlockedOrCloseIfLoggedOut:YES]; + [self updateElements]; + }]; + } forKeyPath:@"key" options:NSKeyValueObservingOptionInitial context:nil]; + [[NSNotificationCenter defaultCenter] addObserverForName:NSWindowDidBecomeKeyNotification object:self.window + queue:[NSOperationQueue mainQueue] usingBlock: + ^(NSNotification *note) { + [self ensureLoadedAndUnlockedOrCloseIfLoggedOut:NO]; + [self.siteField selectText:nil]; + [self updateElements]; + }]; + [[NSNotificationCenter defaultCenter] addObserverForName:NSWindowWillCloseNotification object:self.window + queue:[NSOperationQueue mainQueue] usingBlock: + ^(NSNotification *note) { + NSWindow *sheet = [self.window attachedSheet]; + if (sheet) + [NSApp endSheet:sheet]; + + [NSApp hide:nil]; + self.closing = NO; + }]; + [[NSNotificationCenter defaultCenter] addObserverForName:MPSignedOutNotification object:nil + queue:[NSOperationQueue mainQueue] usingBlock: + ^(NSNotification *note) { + self.userLabel.stringValue = @""; + self.siteField.stringValue = @""; + self.elements = nil; + + [self ensureLoadedAndUnlockedOrCloseIfLoggedOut:YES]; + }]; + [[NSNotificationCenter defaultCenter] addObserverForName:MPSignedInNotification object:nil + queue:[NSOperationQueue mainQueue] usingBlock: + ^(NSNotification *note) { + self.userLabel.stringValue = PearlString( @"%@'s password for:", + [[MPMacAppDelegate get] activeUserForMainThread].name ); + }]; + [[NSNotificationCenter defaultCenter] addObserverForName:USMStoreDidChangeNotification object:nil + queue:[NSOperationQueue mainQueue] usingBlock: + ^(NSNotification *note) { + [self ensureLoadedAndUnlockedOrCloseIfLoggedOut:NO]; + }]; + + [super windowDidLoad]; +} + +- (void)close { + + self.closing = YES; + [super close]; +} + +#pragma mark - NSAlert + +- (void)alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo { + + if (contextInfo == MPAlertIncorrectMP) { + [self close]; + return; + } + if (contextInfo == MPAlertUnlockMP) { + switch (returnCode) { + case NSAlertFirstButtonReturn: { + // "Unlock" button. + self.contentContainer.alphaValue = 0; + self.inProgress = YES; + + NSString *password = [(NSSecureTextField *)alert.accessoryView stringValue]; + [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) { + MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserInContext:moc]; + NSString *userName = activeUser.name; + BOOL success = [[MPMacAppDelegate get] signInAsUser:activeUser saveInContext:moc + usingMasterPassword:password]; + self.inProgress = NO; + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + if (success) + self.contentContainer.alphaValue = 1; + else { + [[NSAlert alertWithError:[NSError errorWithDomain:MPErrorDomain code:0 userInfo:@{ + NSLocalizedDescriptionKey : PearlString( @"Incorrect master password for user %@", userName ) + }]] beginSheetModalForWindow:self.window modalDelegate:self + didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:MPAlertIncorrectMP]; + } + }]; + }]; + break; + } + + case NSAlertSecondButtonReturn: { + // "Change" button. + NSAlert *alert_ = [NSAlert new]; + [alert_ addButtonWithTitle:@"Update"]; + [alert_ addButtonWithTitle:@"Cancel"]; + [alert_ setMessageText:@"Changing Master Password"]; + [alert_ setInformativeText:@"This will allow you to log in with a different master password.\n\n" + @"Note that you will only see the sites and passwords for the master password you log in with.\n" + @"If you log in with a different master password, your current sites will be unavailable.\n\n" + @"You can always change back to your current master password later.\n" + @"Your current sites and passwords will then become available again."]; + + if ([alert_ runModal] == NSAlertFirstButtonReturn) { + [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { + MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserInContext:context]; + activeUser.keyID = nil; + [[MPMacAppDelegate get] forgetSavedKeyFor:activeUser]; + [[MPMacAppDelegate get] signOutAnimated:YES]; + [context saveToStore]; + }]; + } + break; + } + + case NSAlertThirdButtonReturn: { + // "Cancel" button. + [self close]; + break; + } + + default: + break; + } + + return; + } + if (contextInfo == MPAlertCreateSite) { + switch (returnCode) { + case NSAlertFirstButtonReturn: { + // "Create" button. + [[MPMacAppDelegate get] addElementNamed:[self.siteField stringValue] completion:^(MPElementEntity *element) { + if (element) + PearlMainQueue( ^{ [self updateElements]; } ); + }]; + break; + } + case NSAlertThirdButtonReturn: + // "Cancel" button. + break; + default: + break; + } + } +} + +#pragma mark - NSCollectionViewDelegate + +#pragma mark - NSTextFieldDelegate +- (void)doCommandBySelector:(SEL)commandSelector { + + if (commandSelector == @selector(insertNewline:)) + [self useSite]; +} + +- (BOOL)control:(NSControl *)control textView:(NSTextView *)fieldEditor doCommandBySelector:(SEL)commandSelector { + + if (commandSelector == @selector(cancel:)) + [self close]; + if (commandSelector == @selector(moveUp:)) + self.elementSelectionIndexes = + [NSIndexSet indexSetWithIndex:MAX(self.elementSelectionIndexes.firstIndex, (NSUInteger)1) - 1]; + if (commandSelector == @selector(moveDown:)) + self.elementSelectionIndexes = + [NSIndexSet indexSetWithIndex:MIN(self.elementSelectionIndexes.firstIndex + 1, self.elements.count - 1)]; + if (commandSelector == @selector(moveLeft:)) + [[self selectedView].animator setBoundsOrigin:NSZeroPoint]; + if (commandSelector == @selector(moveRight:)) + [[self selectedView].animator setBoundsOrigin:NSMakePoint( self.siteCollectionView.frame.size.width / 2, 0 )]; + if (commandSelector == @selector(insertNewline:)) + [self useSite]; + else + return NO; + + return YES; +} + +- (void)controlTextDidChange:(NSNotification *)note { + + if (note.object != self.siteField) + return; + + // Update the site content as the site name changes. + if ([[NSApp currentEvent] type] == NSKeyDown && + [[[NSApp currentEvent] charactersIgnoringModifiers] isEqualToString:@"\r"]) { // Return while completing. + [self useSite]; + return; + } + +// if ([[NSApp currentEvent] type] == NSKeyDown && +// [[[NSApp currentEvent] charactersIgnoringModifiers] characterAtIndex:0] == 0x1b) { // Escape while completing. +// [self trySiteWithAction:NO]; +// return; +// } + + [self updateElements]; +} + +#pragma mark - Private + +- (BOOL)ensureLoadedAndUnlockedOrCloseIfLoggedOut:(BOOL)closeIfLoggedOut { + + if (![self ensureStoreLoaded]) + return NO; + + if (self.closing || self.inProgress || !self.window.isKeyWindow) + return NO; + + return [self ensureUnlocked:closeIfLoggedOut]; +} + +- (BOOL)ensureStoreLoaded { + + if ([MPMacAppDelegate managedObjectContextForMainThreadIfReady]) { + // Store loaded. + if (self.loadingDataAlert.window) + [NSApp endSheet:self.loadingDataAlert.window]; + + return YES; + } + + [self.loadingDataAlert = [NSAlert alertWithMessageText:@"Opening Your Data" defaultButton:@"..." alternateButton:nil otherButton:nil + informativeTextWithFormat:@""] + beginSheetModalForWindow:self.window modalDelegate:self + didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:nil]; + + return NO; +} + +- (BOOL)ensureUnlocked:(BOOL)closeIfLoggedOut { + + __block BOOL unlocked = NO; + [MPMacAppDelegate managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *moc) { + MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserInContext:moc]; + NSString *userName = activeUser.name; + if (!activeUser) { + // No user to sign in with. + if (closeIfLoggedOut) + [self close]; + return; + } + if ([MPMacAppDelegate get].key) { + // Already logged in. + unlocked = YES; + return; + } + if (activeUser.saveKey && closeIfLoggedOut) { + // App was locked, don't instantly unlock again. + [self close]; + return; + } + if ([[MPMacAppDelegate get] signInAsUser:activeUser saveInContext:moc usingMasterPassword:nil]) { + // Loaded the key from the keychain. + unlocked = YES; + return; + } + + // Ask the user to set the key through his master password. + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + if ([MPMacAppDelegate get].key) + return; + + [self.siteField setStringValue:@""]; + + NSAlert *alert = [NSAlert new]; + [alert addButtonWithTitle:@"Unlock"]; + [alert addButtonWithTitle:@"Change"]; + [alert addButtonWithTitle:@"Cancel"]; + [alert setMessageText:@"Master Password is locked."]; + [alert setInformativeText:PearlString( @"The master password is required to unlock the application for:\n\n%@", userName )]; + + NSSecureTextField *passwordField = [[NSSecureTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )]; + [alert setAccessoryView:passwordField]; + [alert layout]; + [alert beginSheetModalForWindow:self.window modalDelegate:self + didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:MPAlertUnlockMP]; + [passwordField becomeFirstResponder]; + }]; + }]; + + return unlocked; +} + +- (void)updateElements { + + if (![MPMacAppDelegate get].key) { + self.elements = nil; + return; + } + + NSString *query = [self.siteField.currentEditor string]; + [MPMacAppDelegate managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) { + NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )]; + fetchRequest.sortDescriptors = [NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:@"lastUsed" ascending:NO]]; + fetchRequest.predicate = [NSPredicate predicateWithFormat:@"(%@ == '' OR name BEGINSWITH[cd] %@) AND user == %@", + query, query, [[MPMacAppDelegate get] activeUserInContext:context]]; + + NSError *error = nil; + NSArray *siteResults = [context executeFetchRequest:fetchRequest error:&error]; + if (!siteResults) { + err(@"While fetching elements for completion: %@", error); + return; + } + + NSMutableArray *newElements = [NSMutableArray arrayWithCapacity:[siteResults count]]; + for (MPElementEntity *element in siteResults) + [newElements addObject:[[MPElementModel alloc] initWithEntity:element]]; + self.elements = newElements; + if (!self.selectedElement) + self.elementSelectionIndexes = [newElements count]? [NSIndexSet indexSetWithIndex:0]: nil; + }]; +} + +- (NSUInteger)selectedIndex { + + if (!self.elementSelectionIndexes) + return NSNotFound; + NSUInteger selectedIndex = self.elementSelectionIndexes.firstIndex; + if (selectedIndex >= self.elements.count) + return NSNotFound; + + return selectedIndex; +} + +- (NSBox *)selectedView { + + NSUInteger selectedIndex = [self selectedIndex]; + if (selectedIndex == NSNotFound) + return nil; + + return (NSBox *)[self.siteCollectionView itemAtIndex:selectedIndex].view; +} + +- (MPElementModel *)selectedElement { + + NSUInteger selectedIndex = [self selectedIndex]; + if (selectedIndex == NSNotFound) + return nil; + + return (MPElementModel *)self.elements[selectedIndex]; +} + +- (void)setSelectedElement:(MPElementModel *)element { + + self.elementSelectionIndexes = [NSIndexSet indexSetWithIndex:[self.elements indexOfObject:element]]; +} + +- (void)useSite { + + MPElementModel *selectedElement = [self selectedElement]; + if (selectedElement) { + // Performing action while content is available. Copy it. + [self copyContent:selectedElement.content]; + + [self close]; + + NSUserNotification *notification = [NSUserNotification new]; + notification.title = @"Password Copied"; + if (selectedElement.loginName.length) + notification.subtitle = PearlString( @"%@ at %@", selectedElement.loginName, selectedElement.site ); + else + notification.subtitle = selectedElement.site; + [[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification]; + } + else { + NSString *siteName = [self.siteField stringValue]; + if ([siteName length]) + // Performing action without content but a site name is written. + [self createNewSite:siteName]; + } +} + +- (void)copyContent:(NSString *)content { + + [[NSPasteboard generalPasteboard] declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil]; + if (![[NSPasteboard generalPasteboard] setString:content forType:NSPasteboardTypeString]) { + wrn(@"Couldn't copy password to pasteboard."); + return; + } + + [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) { + [[self.selectedElement entityInContext:moc] use]; + [moc saveToStore]; + }]; +} + +- (void)createNewSite:(NSString *)siteName { + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + NSAlert *alert = [NSAlert new]; + [alert addButtonWithTitle:@"Create"]; + [alert addButtonWithTitle:@"Cancel"]; + [alert setMessageText:@"Create site?"]; + [alert setInformativeText:PearlString( @"Do you want to create a new site named:\n\n%@", siteName )]; + [alert beginSheetModalForWindow:self.window modalDelegate:self + didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:MPAlertCreateSite]; + }]; +} + +#pragma mark - KVO + +- (void)setElementSelectionIndexes:(NSIndexSet *)elementSelectionIndexes { + + // First reset bounds. + PearlMainQueue(^{ + NSUInteger selectedIndex = self.elementSelectionIndexes.firstIndex; + if (selectedIndex != NSNotFound && selectedIndex < self.elements.count) + [[self selectedView].animator setBoundsOrigin:NSZeroPoint]; + } ); + + _elementSelectionIndexes = elementSelectionIndexes; +} + +- (void)insertObject:(MPElementModel *)model inElementsAtIndex:(NSUInteger)index { + + [self.elements insertObject:model atIndex:index]; +} + +- (void)removeObjectFromElementsAtIndex:(NSUInteger)index { + + [self.elements removeObjectAtIndex:index]; +} + +@end diff --git a/MasterPassword/ObjC/Mac/MPPasswordDialogController.xib b/MasterPassword/ObjC/Mac/MPPasswordDialogController.xib new file mode 100644 index 00000000..0d15062f --- /dev/null +++ b/MasterPassword/ObjC/Mac/MPPasswordDialogController.xib @@ -0,0 +1,380 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NSNegateBoolean + + + + + + diff --git a/MasterPassword/ObjC/Mac/MPPasswordWindow.h b/MasterPassword/ObjC/Mac/MPPasswordWindow.h new file mode 100644 index 00000000..b3beab8c --- /dev/null +++ b/MasterPassword/ObjC/Mac/MPPasswordWindow.h @@ -0,0 +1,23 @@ +/** + * Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com) + * + * See the enclosed file LICENSE for license information (LGPLv3). If you did + * not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt + * + * @author Maarten Billemont + * @license http://www.gnu.org/licenses/lgpl-3.0.txt + */ + +// +// MPPasswordWindow.h +// MPPasswordWindow +// +// Created by lhunath on 2014-06-19. +// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved. +// + +#import + + +@interface MPPasswordWindow : NSWindow +@end diff --git a/MasterPassword/ObjC/Mac/MPPasswordWindow.m b/MasterPassword/ObjC/Mac/MPPasswordWindow.m new file mode 100644 index 00000000..fa1e4ea0 --- /dev/null +++ b/MasterPassword/ObjC/Mac/MPPasswordWindow.m @@ -0,0 +1,43 @@ +/** + * Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com) + * + * See the enclosed file LICENSE for license information (LGPLv3). If you did + * not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt + * + * @author Maarten Billemont + * @license http://www.gnu.org/licenses/lgpl-3.0.txt + */ + +// +// MPPasswordWindow.h +// MPPasswordWindow +// +// Created by lhunath on 2014-06-19. +// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved. +// + +#import "MPPasswordWindow.h" + + +@implementation MPPasswordWindow + +#pragma mark - Life + +- (void)awakeFromNib { + + [super awakeFromNib]; + + self.level = NSScreenSaverWindowLevel; + self.alphaValue = 0; +} + +- (BOOL)canBecomeKeyWindow { + + return YES; +} + +#pragma mark - State + +#pragma mark - Private + +@end diff --git a/MasterPassword/ObjC/Mac/MPPasswordWindowController.h b/MasterPassword/ObjC/Mac/MPPasswordWindowController.h index 374b86a7..547fddcc 100644 --- a/MasterPassword/ObjC/Mac/MPPasswordWindowController.h +++ b/MasterPassword/ObjC/Mac/MPPasswordWindowController.h @@ -1,23 +1,27 @@ +/** + * Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com) + * + * See the enclosed file LICENSE for license information (LGPLv3). If you did + * not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt + * + * @author Maarten Billemont + * @license http://www.gnu.org/licenses/lgpl-3.0.txt + */ + // // MPPasswordWindowController.h -// MasterPassword-Mac +// MPPasswordWindowController // -// Created by Maarten Billemont on 04/03/12. -// Copyright (c) 2012 Lyndir. All rights reserved. +// Created by lhunath on 2014-06-18. +// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved. // #import -@class MPElementModel; @interface MPPasswordWindowController : NSWindowController -@property(nonatomic, strong) NSMutableArray *elements; -@property(nonatomic, strong) NSIndexSet *elementSelectionIndexes; -@property(nonatomic, weak) IBOutlet NSTextField *siteField; -@property(nonatomic, weak) IBOutlet NSView *contentContainer; +@property(nonatomic, weak) IBOutlet NSImageView *blurView; @property(nonatomic, weak) IBOutlet NSTextField *userLabel; -@property(nonatomic, weak) IBOutlet NSCollectionView *siteCollectionView; - -- (void)updateElements; +@property(nonatomic, weak) IBOutlet NSSearchField *siteField; @end diff --git a/MasterPassword/ObjC/Mac/MPPasswordWindowController.m b/MasterPassword/ObjC/Mac/MPPasswordWindowController.m index 7b038757..297e1242 100644 --- a/MasterPassword/ObjC/Mac/MPPasswordWindowController.m +++ b/MasterPassword/ObjC/Mac/MPPasswordWindowController.m @@ -1,30 +1,24 @@ +/** +* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com) +* +* See the enclosed file LICENSE for license information (LGPLv3). If you did +* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt +* +* @author Maarten Billemont +* @license http://www.gnu.org/licenses/lgpl-3.0.txt +*/ + // -// MPPasswordWindowController.m -// MasterPassword-Mac +// MPPasswordWindowController.h +// MPPasswordWindowController // -// Created by Maarten Billemont on 04/03/12. -// Copyright (c) 2012 Lyndir. All rights reserved. +// Created by lhunath on 2014-06-18. +// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved. // #import "MPPasswordWindowController.h" #import "MPMacAppDelegate.h" -#import "MPAppDelegate_Key.h" #import "MPAppDelegate_Store.h" -#import "MPElementModel.h" - -#define MPAlertUnlockMP @"MPAlertUnlockMP" -#define MPAlertIncorrectMP @"MPAlertIncorrectMP" -#define MPAlertCreateSite @"MPAlertCreateSite" - -@interface MPPasswordWindowController() - -@property(nonatomic) BOOL inProgress; - -@property(nonatomic, strong) NSOperationQueue *backgroundQueue; -@property(nonatomic, strong) NSAlert *loadingDataAlert; -@property(nonatomic) BOOL closing; - -@end @implementation MPPasswordWindowController @@ -32,446 +26,78 @@ - (void)windowDidLoad { - if ([[MPMacConfig get].dialogStyleHUD boolValue]) { - self.window.styleMask = NSHUDWindowMask | NSTitledWindowMask | NSUtilityWindowMask | NSClosableWindowMask; - self.userLabel.textColor = [NSColor whiteColor]; - } - else { - self.window.styleMask = NSTexturedBackgroundWindowMask | NSResizableWindowMask | NSTitledWindowMask | NSClosableWindowMask; - self.userLabel.textColor = [NSColor controlTextColor]; - } - - self.backgroundQueue = [NSOperationQueue new]; - self.backgroundQueue.maxConcurrentOperationCount = 1; - - [[MPMacAppDelegate get] addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) { -// [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) { -// if (![MPAlgorithmDefault migrateUser:[[MPMacAppDelegate get] activeUserInContext:moc]]) -// [NSAlert alertWithMessageText:@"Migration Needed" defaultButton:@"OK" alternateButton:nil otherButton:nil -// informativeTextWithFormat:@"Certain sites require explicit migration to get updated to the latest version of the " -// @"Master Password algorithm. For these sites, a migration button will appear. Migrating these sites will cause " -// @"their passwords to change. You'll need to update your profile for that site with the new password."]; -// [moc saveToStore]; -// }]; - [[NSOperationQueue mainQueue] addOperationWithBlock:^{ - [self ensureLoadedAndUnlockedOrCloseIfLoggedOut:YES]; - [self updateElements]; - }]; - } forKeyPath:@"key" options:NSKeyValueObservingOptionInitial context:nil]; - [[NSNotificationCenter defaultCenter] addObserverForName:NSWindowDidBecomeKeyNotification object:self.window - queue:[NSOperationQueue mainQueue] usingBlock: - ^(NSNotification *note) { - [self ensureLoadedAndUnlockedOrCloseIfLoggedOut:NO]; - [self.siteField selectText:nil]; - [self updateElements]; - }]; - [[NSNotificationCenter defaultCenter] addObserverForName:NSWindowWillCloseNotification object:self.window - queue:[NSOperationQueue mainQueue] usingBlock: - ^(NSNotification *note) { - NSWindow *sheet = [self.window attachedSheet]; - if (sheet) - [NSApp endSheet:sheet]; - - [NSApp hide:nil]; - self.closing = NO; - }]; - [[NSNotificationCenter defaultCenter] addObserverForName:MPSignedOutNotification object:nil - queue:[NSOperationQueue mainQueue] usingBlock: - ^(NSNotification *note) { - self.userLabel.stringValue = @""; - self.siteField.stringValue = @""; - self.elements = nil; - - [self ensureLoadedAndUnlockedOrCloseIfLoggedOut:YES]; - }]; - [[NSNotificationCenter defaultCenter] addObserverForName:MPSignedInNotification object:nil - queue:[NSOperationQueue mainQueue] usingBlock: - ^(NSNotification *note) { - self.userLabel.stringValue = PearlString( @"%@'s password for:", - [[MPMacAppDelegate get] activeUserForMainThread].name ); - }]; - [[NSNotificationCenter defaultCenter] addObserverForName:USMStoreDidChangeNotification object:nil - queue:[NSOperationQueue mainQueue] usingBlock: - ^(NSNotification *note) { - [self ensureLoadedAndUnlockedOrCloseIfLoggedOut:NO]; - }]; - [super windowDidLoad]; + +// [[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationWillBecomeActiveNotification object:nil +// queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { +// [self fadeIn]; +// }]; +// [[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationWillResignActiveNotification object:nil +// queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { +// [self fadeOut]; +// }]; + [[NSNotificationCenter defaultCenter] addObserverForName:NSWindowDidBecomeKeyNotification object:self.window + queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { + [self fadeIn]; + [self.siteField becomeFirstResponder]; + }]; + [[NSNotificationCenter defaultCenter] addObserverForName:NSWindowDidResignKeyNotification object:self.window + queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { + [self fadeOut]; + }]; + [[NSNotificationCenter defaultCenter] addObserverForName:MPSignedInNotification object:nil + queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { + [self updateUser]; + }]; + [[NSNotificationCenter defaultCenter] addObserverForName:MPSignedOutNotification object:nil + queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { + [self updateUser]; + }]; } -- (void)close { +- (void)updateUser { - self.closing = YES; - [super close]; + [MPMacAppDelegate managedObjectContextForMainThreadPerformBlock:^(NSManagedObjectContext *mainContext) { + MPUserEntity *mainActiveUser = [[MPMacAppDelegate get] activeUserInContext:mainContext]; + if (mainActiveUser) + self.userLabel.stringValue = strf( @"%@'s password for:", mainActiveUser.name ); + else + self.userLabel.stringValue = @""; + }]; } -#pragma mark - NSAlert - -- (void)alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo { - - if (contextInfo == MPAlertIncorrectMP) { - [self close]; - return; - } - if (contextInfo == MPAlertUnlockMP) { - switch (returnCode) { - case NSAlertFirstButtonReturn: { - // "Unlock" button. - self.contentContainer.alphaValue = 0; - self.inProgress = YES; - - NSString *password = [(NSSecureTextField *)alert.accessoryView stringValue]; - [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) { - MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserInContext:moc]; - NSString *userName = activeUser.name; - BOOL success = [[MPMacAppDelegate get] signInAsUser:activeUser saveInContext:moc - usingMasterPassword:password]; - self.inProgress = NO; - - [[NSOperationQueue mainQueue] addOperationWithBlock:^{ - if (success) - self.contentContainer.alphaValue = 1; - else { - [[NSAlert alertWithError:[NSError errorWithDomain:MPErrorDomain code:0 userInfo:@{ - NSLocalizedDescriptionKey : PearlString( @"Incorrect master password for user %@", userName ) - }]] beginSheetModalForWindow:self.window modalDelegate:self - didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:MPAlertIncorrectMP]; - } - }]; - }]; - break; - } - - case NSAlertSecondButtonReturn: { - // "Change" button. - NSAlert *alert_ = [NSAlert new]; - [alert_ addButtonWithTitle:@"Update"]; - [alert_ addButtonWithTitle:@"Cancel"]; - [alert_ setMessageText:@"Changing Master Password"]; - [alert_ setInformativeText:@"This will allow you to log in with a different master password.\n\n" - @"Note that you will only see the sites and passwords for the master password you log in with.\n" - @"If you log in with a different master password, your current sites will be unavailable.\n\n" - @"You can always change back to your current master password later.\n" - @"Your current sites and passwords will then become available again."]; - - if ([alert_ runModal] == NSAlertFirstButtonReturn) { - [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { - MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserInContext:context]; - activeUser.keyID = nil; - [[MPMacAppDelegate get] forgetSavedKeyFor:activeUser]; - [[MPMacAppDelegate get] signOutAnimated:YES]; - [context saveToStore]; - }]; - } - break; - } - - case NSAlertThirdButtonReturn: { - // "Cancel" button. - [self close]; - break; - } - - default: - break; - } - - return; - } - if (contextInfo == MPAlertCreateSite) { - switch (returnCode) { - case NSAlertFirstButtonReturn: { - // "Create" button. - [[MPMacAppDelegate get] addElementNamed:[self.siteField stringValue] completion:^(MPElementEntity *element) { - if (element) - PearlMainQueue( ^{ [self updateElements]; } ); - }]; - break; - } - case NSAlertThirdButtonReturn: - // "Cancel" button. - break; - default: - break; - } - } -} - -#pragma mark - NSCollectionViewDelegate - -#pragma mark - NSTextFieldDelegate -- (void)doCommandBySelector:(SEL)commandSelector { - - if (commandSelector == @selector(insertNewline:)) - [self useSite]; -} - -- (BOOL)control:(NSControl *)control textView:(NSTextView *)fieldEditor doCommandBySelector:(SEL)commandSelector { - - if (commandSelector == @selector(cancel:)) - [self close]; - if (commandSelector == @selector(moveUp:)) - self.elementSelectionIndexes = - [NSIndexSet indexSetWithIndex:MAX(self.elementSelectionIndexes.firstIndex, (NSUInteger)1) - 1]; - if (commandSelector == @selector(moveDown:)) - self.elementSelectionIndexes = - [NSIndexSet indexSetWithIndex:MIN(self.elementSelectionIndexes.firstIndex + 1, self.elements.count - 1)]; - if (commandSelector == @selector(moveLeft:)) - [[self selectedView].animator setBoundsOrigin:NSZeroPoint]; - if (commandSelector == @selector(moveRight:)) - [[self selectedView].animator setBoundsOrigin:NSMakePoint( self.siteCollectionView.frame.size.width / 2, 0 )]; - if (commandSelector == @selector(insertNewline:)) - [self useSite]; - else - return NO; - - return YES; -} - -- (void)controlTextDidChange:(NSNotification *)note { - - if (note.object != self.siteField) - return; - - // Update the site content as the site name changes. - if ([[NSApp currentEvent] type] == NSKeyDown && - [[[NSApp currentEvent] charactersIgnoringModifiers] isEqualToString:@"\r"]) { // Return while completing. - [self useSite]; - return; - } - -// if ([[NSApp currentEvent] type] == NSKeyDown && -// [[[NSApp currentEvent] charactersIgnoringModifiers] characterAtIndex:0] == 0x1b) { // Escape while completing. -// [self trySiteWithAction:NO]; -// return; -// } - - [self updateElements]; -} +#pragma mark - State #pragma mark - Private -- (BOOL)ensureLoadedAndUnlockedOrCloseIfLoggedOut:(BOOL)closeIfLoggedOut { +- (void)fadeIn { - if (![self ensureStoreLoaded]) - return NO; + CGWindowID windowID = (CGWindowID)[self.window windowNumber]; + CGImageRef capturedImage = CGWindowListCreateImage( CGRectInfinite, kCGWindowListOptionOnScreenBelowWindow, windowID, + kCGWindowImageBoundsIgnoreFraming ); + NSImage *screenImage = [[NSImage alloc] initWithCGImage:capturedImage size:NSMakeSize( + CGImageGetWidth( capturedImage ) / self.window.backingScaleFactor, + CGImageGetHeight( capturedImage ) / self.window.backingScaleFactor )]; - if (self.closing || self.inProgress || !self.window.isKeyWindow) - return NO; + NSImage *smallImage = [[NSImage alloc] initWithSize:NSMakeSize( + CGImageGetWidth( capturedImage ) / 20, + CGImageGetHeight( capturedImage ) / 20 )]; + [smallImage lockFocus]; + [screenImage drawInRect:(NSRect){ .origin = CGPointZero, .size = smallImage.size, } + fromRect:NSZeroRect + operation:NSCompositeSourceOver + fraction:1.0]; + [smallImage unlockFocus]; - return [self ensureUnlocked:closeIfLoggedOut]; + self.blurView.image = smallImage; + + [self.window setFrame:self.window.screen.frame display:YES]; + [[self.window animator] setAlphaValue:1.0]; } -- (BOOL)ensureStoreLoaded { +- (void)fadeOut { - if ([MPMacAppDelegate managedObjectContextForMainThreadIfReady]) { - // Store loaded. - if (self.loadingDataAlert.window) - [NSApp endSheet:self.loadingDataAlert.window]; - - return YES; - } - - [self.loadingDataAlert = [NSAlert alertWithMessageText:@"Opening Your Data" defaultButton:@"..." alternateButton:nil otherButton:nil - informativeTextWithFormat:@""] - beginSheetModalForWindow:self.window modalDelegate:self - didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:nil]; - - return NO; -} - -- (BOOL)ensureUnlocked:(BOOL)closeIfLoggedOut { - - __block BOOL unlocked = NO; - [MPMacAppDelegate managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *moc) { - MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserInContext:moc]; - NSString *userName = activeUser.name; - if (!activeUser) { - // No user to sign in with. - if (closeIfLoggedOut) - [self close]; - return; - } - if ([MPMacAppDelegate get].key) { - // Already logged in. - unlocked = YES; - return; - } - if (activeUser.saveKey && closeIfLoggedOut) { - // App was locked, don't instantly unlock again. - [self close]; - return; - } - if ([[MPMacAppDelegate get] signInAsUser:activeUser saveInContext:moc usingMasterPassword:nil]) { - // Loaded the key from the keychain. - unlocked = YES; - return; - } - - // Ask the user to set the key through his master password. - [[NSOperationQueue mainQueue] addOperationWithBlock:^{ - if ([MPMacAppDelegate get].key) - return; - - [self.siteField setStringValue:@""]; - - NSAlert *alert = [NSAlert new]; - [alert addButtonWithTitle:@"Unlock"]; - [alert addButtonWithTitle:@"Change"]; - [alert addButtonWithTitle:@"Cancel"]; - [alert setMessageText:@"Master Password is locked."]; - [alert setInformativeText:PearlString( @"The master password is required to unlock the application for:\n\n%@", userName )]; - - NSSecureTextField *passwordField = [[NSSecureTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )]; - [alert setAccessoryView:passwordField]; - [alert layout]; - [alert beginSheetModalForWindow:self.window modalDelegate:self - didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:MPAlertUnlockMP]; - [passwordField becomeFirstResponder]; - }]; - }]; - - return unlocked; -} - -- (void)updateElements { - - if (![MPMacAppDelegate get].key) { - self.elements = nil; - return; - } - - NSString *query = [self.siteField.currentEditor string]; - [MPMacAppDelegate managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) { - NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )]; - fetchRequest.sortDescriptors = [NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:@"lastUsed" ascending:NO]]; - fetchRequest.predicate = [NSPredicate predicateWithFormat:@"(%@ == '' OR name BEGINSWITH[cd] %@) AND user == %@", - query, query, [[MPMacAppDelegate get] activeUserInContext:context]]; - - NSError *error = nil; - NSArray *siteResults = [context executeFetchRequest:fetchRequest error:&error]; - if (!siteResults) { - err(@"While fetching elements for completion: %@", error); - return; - } - - NSMutableArray *newElements = [NSMutableArray arrayWithCapacity:[siteResults count]]; - for (MPElementEntity *element in siteResults) - [newElements addObject:[[MPElementModel alloc] initWithEntity:element]]; - self.elements = newElements; - if (!self.selectedElement) - self.elementSelectionIndexes = [newElements count]? [NSIndexSet indexSetWithIndex:0]: nil; - }]; -} - -- (NSUInteger)selectedIndex { - - if (!self.elementSelectionIndexes) - return NSNotFound; - NSUInteger selectedIndex = self.elementSelectionIndexes.firstIndex; - if (selectedIndex >= self.elements.count) - return NSNotFound; - - return selectedIndex; -} - -- (NSBox *)selectedView { - - NSUInteger selectedIndex = [self selectedIndex]; - if (selectedIndex == NSNotFound) - return nil; - - return (NSBox *)[self.siteCollectionView itemAtIndex:selectedIndex].view; -} - -- (MPElementModel *)selectedElement { - - NSUInteger selectedIndex = [self selectedIndex]; - if (selectedIndex == NSNotFound) - return nil; - - return (MPElementModel *)self.elements[selectedIndex]; -} - -- (void)setSelectedElement:(MPElementModel *)element { - - self.elementSelectionIndexes = [NSIndexSet indexSetWithIndex:[self.elements indexOfObject:element]]; -} - -- (void)useSite { - - MPElementModel *selectedElement = [self selectedElement]; - if (selectedElement) { - // Performing action while content is available. Copy it. - [self copyContent:selectedElement.content]; - - [self close]; - - NSUserNotification *notification = [NSUserNotification new]; - notification.title = @"Password Copied"; - if (selectedElement.loginName.length) - notification.subtitle = PearlString( @"%@ at %@", selectedElement.loginName, selectedElement.site ); - else - notification.subtitle = selectedElement.site; - [[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification]; - } - else { - NSString *siteName = [self.siteField stringValue]; - if ([siteName length]) - // Performing action without content but a site name is written. - [self createNewSite:siteName]; - } -} - -- (void)copyContent:(NSString *)content { - - [[NSPasteboard generalPasteboard] declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil]; - if (![[NSPasteboard generalPasteboard] setString:content forType:NSPasteboardTypeString]) { - wrn(@"Couldn't copy password to pasteboard."); - return; - } - - [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) { - [[self.selectedElement entityInContext:moc] use]; - [moc saveToStore]; - }]; -} - -- (void)createNewSite:(NSString *)siteName { - - [[NSOperationQueue mainQueue] addOperationWithBlock:^{ - NSAlert *alert = [NSAlert new]; - [alert addButtonWithTitle:@"Create"]; - [alert addButtonWithTitle:@"Cancel"]; - [alert setMessageText:@"Create site?"]; - [alert setInformativeText:PearlString( @"Do you want to create a new site named:\n\n%@", siteName )]; - [alert beginSheetModalForWindow:self.window modalDelegate:self - didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:MPAlertCreateSite]; - }]; -} - -#pragma mark - KVO - -- (void)setElementSelectionIndexes:(NSIndexSet *)elementSelectionIndexes { - - // First reset bounds. - PearlMainQueue(^{ - NSUInteger selectedIndex = self.elementSelectionIndexes.firstIndex; - if (selectedIndex != NSNotFound && selectedIndex < self.elements.count) - [[self selectedView].animator setBoundsOrigin:NSZeroPoint]; - } ); - - _elementSelectionIndexes = elementSelectionIndexes; -} - -- (void)insertObject:(MPElementModel *)model inElementsAtIndex:(NSUInteger)index { - - [self.elements insertObject:model atIndex:index]; -} - -- (void)removeObjectFromElementsAtIndex:(NSUInteger)index { - - [self.elements removeObjectAtIndex:index]; + [[self.window animator] setAlphaValue:0.0]; } @end diff --git a/MasterPassword/ObjC/Mac/MPPasswordWindowController.xib b/MasterPassword/ObjC/Mac/MPPasswordWindowController.xib index 193e2a08..063eef45 100644 --- a/MasterPassword/ObjC/Mac/MPPasswordWindowController.xib +++ b/MasterPassword/ObjC/Mac/MPPasswordWindowController.xib @@ -7,374 +7,203 @@ - - - - - + + + - - - - - - - - + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - NSNegateBoolean - - - - + + + diff --git a/MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/project.pbxproj b/MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/project.pbxproj index a4802b08..b722d449 100644 --- a/MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/project.pbxproj +++ b/MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/project.pbxproj @@ -7,12 +7,14 @@ objects = { /* Begin PBXBuildFile section */ + 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 */; }; 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 */; }; 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 */; }; DA0933D01747B91B00DE1CEF /* appstore.png in Resources */ = {isa = PBXBuildFile; fileRef = DA0933CF1747B91B00DE1CEF /* appstore.png */; }; DA16B33F170661D4000A0EAB /* libUbiquityStoreManager.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DA4425CB1557BED40052177D /* libUbiquityStoreManager.a */; }; @@ -21,6 +23,10 @@ DA16B344170661EE000A0EAB /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA16B343170661EE000A0EAB /* Cocoa.framework */; }; DA16B345170661F2000A0EAB /* libPearl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC77CAD148291A600BCF976 /* libPearl.a */; }; DA1E4D50176E0E280065E0EF /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DA1E4D4F176E0E280065E0EF /* Media.xcassets */; }; + DA2508F119511D3600AC23F1 /* MPPasswordWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA2508F019511D3600AC23F1 /* MPPasswordWindowController.xib */; }; + DA2508F719513C1400AC23F1 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA16B343170661EE000A0EAB /* Cocoa.framework */; }; + DA250924195148B300AC23F1 /* libRMBlurredView.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DA2508F619513C1400AC23F1 /* libRMBlurredView.a */; }; + DA250925195148E200AC23F1 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAEBC45214F6364500987BF6 /* QuartzCore.framework */; }; DA2CA4ED18D323D3007798F8 /* NSError+PearlFullDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = DA2CA4E718D323D3007798F8 /* NSError+PearlFullDescription.m */; }; DA2CA4EE18D323D3007798F8 /* NSError+PearlFullDescription.h in Headers */ = {isa = PBXBuildFile; fileRef = DA2CA4E818D323D3007798F8 /* NSError+PearlFullDescription.h */; }; DA2CA4EF18D323D3007798F8 /* NSArray+Pearl.m in Sources */ = {isa = PBXBuildFile; fileRef = DA2CA4E918D323D3007798F8 /* NSArray+Pearl.m */; }; @@ -57,8 +63,8 @@ DA5E5D021724A667003798D8 /* MPUserEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CB11724A667003798D8 /* MPUserEntity.m */; }; DA5E5D031724A667003798D8 /* MPMacAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CB41724A667003798D8 /* MPMacAppDelegate.m */; }; DA5E5D041724A667003798D8 /* MPMacConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CB61724A667003798D8 /* MPMacConfig.m */; }; - DA5E5D051724A667003798D8 /* MPPasswordWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CB81724A667003798D8 /* MPPasswordWindowController.m */; }; - DA5E5D061724A667003798D8 /* MPPasswordWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA5E5CB91724A667003798D8 /* MPPasswordWindowController.xib */; }; + DA5E5D051724A667003798D8 /* MPPasswordDialogController.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CB81724A667003798D8 /* MPPasswordDialogController.m */; }; + DA5E5D061724A667003798D8 /* MPPasswordDialogController.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA5E5CB91724A667003798D8 /* MPPasswordDialogController.xib */; }; DA5E5D081724A667003798D8 /* MasterPassword.entitlements in Resources */ = {isa = PBXBuildFile; fileRef = DA5E5CBF1724A667003798D8 /* MasterPassword.entitlements */; }; DA5E5D091724A667003798D8 /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = DA5E5CC01724A667003798D8 /* Credits.rtf */; }; DA5E5D0A1724A667003798D8 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = DA5E5CC21724A667003798D8 /* InfoPlist.strings */; }; @@ -300,11 +306,15 @@ /* Begin PBXFileReference section */ 93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Indexing.m"; sourceTree = ""; }; 93D39240B5143E01F0B75E96 /* MPElementModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementModel.h; sourceTree = ""; }; + 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 = ""; }; 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 = ""; }; 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 = ""; }; @@ -312,6 +322,14 @@ DA16B340170661DB000A0EAB /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; }; DA16B343170661EE000A0EAB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; DA1E4D4F176E0E280065E0EF /* Media.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Media.xcassets; path = MasterPassword/Media.xcassets; sourceTree = ""; }; + DA2508F019511D3600AC23F1 /* MPPasswordWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MPPasswordWindowController.xib; sourceTree = ""; }; + DA2508F619513C1400AC23F1 /* libRMBlurredView.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRMBlurredView.a; sourceTree = BUILT_PRODUCTS_DIR; }; + DA2508F919513C1400AC23F1 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + DA2508FA19513C1400AC23F1 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; + DA2508FB19513C1400AC23F1 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; + DA25090719513C1400AC23F1 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; + DA2509261951B86C00AC23F1 /* small-screen.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "small-screen.png"; sourceTree = ""; }; + DA2509271951B86C00AC23F1 /* screen.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = screen.png; sourceTree = ""; }; DA2CA4E718D323D3007798F8 /* NSError+PearlFullDescription.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSError+PearlFullDescription.m"; sourceTree = ""; }; DA2CA4E818D323D3007798F8 /* NSError+PearlFullDescription.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSError+PearlFullDescription.h"; sourceTree = ""; }; DA2CA4E918D323D3007798F8 /* NSArray+Pearl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Pearl.m"; sourceTree = ""; }; @@ -369,9 +387,9 @@ DA5E5CB41724A667003798D8 /* MPMacAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPMacAppDelegate.m; sourceTree = ""; }; DA5E5CB51724A667003798D8 /* MPMacConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPMacConfig.h; sourceTree = ""; }; DA5E5CB61724A667003798D8 /* MPMacConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPMacConfig.m; sourceTree = ""; }; - DA5E5CB71724A667003798D8 /* MPPasswordWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordWindowController.h; sourceTree = ""; }; - DA5E5CB81724A667003798D8 /* MPPasswordWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordWindowController.m; sourceTree = ""; }; - DA5E5CB91724A667003798D8 /* MPPasswordWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MPPasswordWindowController.xib; sourceTree = ""; }; + DA5E5CB71724A667003798D8 /* MPPasswordDialogController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordDialogController.h; sourceTree = ""; }; + DA5E5CB81724A667003798D8 /* MPPasswordDialogController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordDialogController.m; sourceTree = ""; }; + DA5E5CB91724A667003798D8 /* MPPasswordDialogController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MPPasswordDialogController.xib; sourceTree = ""; }; DA5E5CBA1724A667003798D8 /* MasterPassword-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "MasterPassword-Info.plist"; sourceTree = ""; }; DA5E5CBE1724A667003798D8 /* MasterPassword-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MasterPassword-Prefix.pch"; sourceTree = ""; }; DA5E5CBF1724A667003798D8 /* MasterPassword.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = MasterPassword.entitlements; sourceTree = ""; }; @@ -587,6 +605,14 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + DA2508F319513C1400AC23F1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DA2508F719513C1400AC23F1 /* Cocoa.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; DA4425C81557BED40052177D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -599,6 +625,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + DA250925195148E200AC23F1 /* QuartzCore.framework in Frameworks */, + DA250924195148B300AC23F1 /* libRMBlurredView.a in Frameworks */, DAD9B5F01762CAA4001835F9 /* ServiceManagement.framework in Frameworks */, DABC6C16175D8E3A000C15D4 /* libRHStatusItemView.a in Frameworks */, DA16B33F170661D4000A0EAB /* libUbiquityStoreManager.a in Frameworks */, @@ -639,6 +667,16 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + DA2508F819513C1400AC23F1 /* Other Frameworks */ = { + isa = PBXGroup; + children = ( + DA2508F919513C1400AC23F1 /* Foundation.framework */, + DA2508FA19513C1400AC23F1 /* CoreData.framework */, + DA2508FB19513C1400AC23F1 /* AppKit.framework */, + ); + name = "Other Frameworks"; + sourceTree = ""; + }; DA3B8447190FC5A900246EEA /* iOS */ = { isa = PBXGroup; children = ( @@ -675,6 +713,7 @@ DAC6326C148680650075AEA5 /* libjrswizzle.a */, DA4425CB1557BED40052177D /* libUbiquityStoreManager.a */, DABC6C01175D8C85000C15D4 /* libRHStatusItemView.a */, + DA2508F619513C1400AC23F1 /* libRMBlurredView.a */, ); name = Products; sourceTree = ""; @@ -695,6 +734,8 @@ DA5BFA4A147E415C00F98B1E /* Foundation.framework */, DA5BFA4C147E415C00F98B1E /* CoreGraphics.framework */, DA5BFA4E147E415C00F98B1E /* CoreData.framework */, + DA25090719513C1400AC23F1 /* XCTest.framework */, + DA2508F819513C1400AC23F1 /* Other Frameworks */, ); name = Frameworks; sourceTree = ""; @@ -755,9 +796,9 @@ DA5E5CB41724A667003798D8 /* MPMacAppDelegate.m */, DA5E5CB51724A667003798D8 /* MPMacConfig.h */, DA5E5CB61724A667003798D8 /* MPMacConfig.m */, - DA5E5CB71724A667003798D8 /* MPPasswordWindowController.h */, - DA5E5CB81724A667003798D8 /* MPPasswordWindowController.m */, - DA5E5CB91724A667003798D8 /* MPPasswordWindowController.xib */, + DA5E5CB71724A667003798D8 /* MPPasswordDialogController.h */, + DA5E5CB81724A667003798D8 /* MPPasswordDialogController.m */, + DA5E5CB91724A667003798D8 /* MPPasswordDialogController.xib */, DA5E5CBA1724A667003798D8 /* MasterPassword-Info.plist */, DA5E5CBE1724A667003798D8 /* MasterPassword-Prefix.pch */, DA5E5CBF1724A667003798D8 /* MasterPassword.entitlements */, @@ -770,6 +811,11 @@ 93D3960D320FF8A072B092E3 /* MPElementCollectionView.h */, 93D39E73BF5CBF8E5B005CD3 /* MPElementModel.m */, 93D39240B5143E01F0B75E96 /* MPElementModel.h */, + DA2508F019511D3600AC23F1 /* MPPasswordWindowController.xib */, + 93D39A57A7823DE98A0FF83C /* MPPasswordWindowController.m */, + 93D392C3918763B3B72CF366 /* MPPasswordWindowController.h */, + 93D39D9D0061FF1159998F06 /* MPPasswordWindow.m */, + 93D3977484534E99F9BA579D /* MPPasswordWindow.h */, ); path = Mac; sourceTree = ""; @@ -842,6 +888,8 @@ DACA23B51705DF7D002C6C22 /* Media */ = { isa = PBXGroup; children = ( + DA2509261951B86C00AC23F1 /* small-screen.png */, + DA2509271951B86C00AC23F1 /* screen.png */, DA0933CF1747B91B00DE1CEF /* appstore.png */, DA0933CB1747AD2D00DE1CEF /* shot-laptop-leaning-iphone.png */, DA5E5D541724F9C8003798D8 /* MasterPassword.iconset */, @@ -1153,6 +1201,13 @@ /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ + DA2508F419513C1400AC23F1 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; DA4425C91557BED40052177D /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -1294,6 +1349,23 @@ /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ + DA2508F519513C1400AC23F1 /* RMBlurredView */ = { + isa = PBXNativeTarget; + buildConfigurationList = DA25091519513C1500AC23F1 /* Build configuration list for PBXNativeTarget "RMBlurredView" */; + buildPhases = ( + DA2508F219513C1400AC23F1 /* Sources */, + DA2508F319513C1400AC23F1 /* Frameworks */, + DA2508F419513C1400AC23F1 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = RMBlurredView; + productName = RMBlurredView; + productReference = DA2508F619513C1400AC23F1 /* libRMBlurredView.a */; + productType = "com.apple.product-type.library.static"; + }; DA4425CA1557BED40052177D /* UbiquityStoreManager */ = { isa = PBXNativeTarget; buildConfigurationList = DA4425D31557BED40052177D /* Build configuration list for PBXNativeTarget "UbiquityStoreManager" */; @@ -1424,6 +1496,7 @@ DAC6326B148680650075AEA5 /* jrswizzle */, DA4425CA1557BED40052177D /* UbiquityStoreManager */, DABC6C00175D8C85000C15D4 /* RHStatusItemView */, + DA2508F519513C1400AC23F1 /* RMBlurredView */, ); }; /* End PBXProject section */ @@ -1457,6 +1530,7 @@ DAF4EF59190A828100023C90 /* Exo2.0-Bold.otf in Resources */, DACA27181705DF81002C6C22 /* avatar-9.png in Resources */, DACA27191705DF81002C6C22 /* avatar-1@2x.png in Resources */, + DA2508F119511D3600AC23F1 /* MPPasswordWindowController.xib in Resources */, DACA271A1705DF81002C6C22 /* avatar-11@2x.png in Resources */, DACA271B1705DF81002C6C22 /* avatar-7@2x.png in Resources */, DACA271C1705DF81002C6C22 /* avatar-17@2x.png in Resources */, @@ -1497,7 +1571,7 @@ DACA296F1705DF81002C6C22 /* Crashlytics.plist in Resources */, DACA29731705E1A8002C6C22 /* ciphers.plist in Resources */, DACA29741705E1A8002C6C22 /* dictionary.lst in Resources */, - DA5E5D061724A667003798D8 /* MPPasswordWindowController.xib in Resources */, + DA5E5D061724A667003798D8 /* MPPasswordDialogController.xib in Resources */, DA5E5D081724A667003798D8 /* MasterPassword.entitlements in Resources */, DA5E5D091724A667003798D8 /* Credits.rtf in Resources */, DA5E5D0A1724A667003798D8 /* InfoPlist.strings in Resources */, @@ -1544,6 +1618,13 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + DA2508F219513C1400AC23F1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; DA4425C71557BED40052177D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -1573,11 +1654,13 @@ DA5E5D021724A667003798D8 /* MPUserEntity.m in Sources */, DA5E5D031724A667003798D8 /* MPMacAppDelegate.m in Sources */, DA5E5D041724A667003798D8 /* MPMacConfig.m in Sources */, - DA5E5D051724A667003798D8 /* MPPasswordWindowController.m in Sources */, + DA5E5D051724A667003798D8 /* MPPasswordDialogController.m in Sources */, DA5E5D0C1724A667003798D8 /* main.m in Sources */, DA5E5D0D1724A667003798D8 /* MasterPassword.xcdatamodeld in Sources */, 93D39C7C2BE7C0E0763B0177 /* MPElementCollectionView.m in Sources */, 93D39C5789EFA607CF788082 /* MPElementModel.m in Sources */, + 93D39F833DEC1C89B2F795AC /* MPPasswordWindowController.m in Sources */, + 93D390C676DF52DA7E459F19 /* MPPasswordWindow.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1686,6 +1769,27 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + DA25091619513C1500AC23F1 /* Debug-Mac */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_ARC = YES; + }; + name = "Debug-Mac"; + }; + DA25091719513C1500AC23F1 /* AdHoc-Mac */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_ARC = YES; + }; + name = "AdHoc-Mac"; + }; + DA25091819513C1500AC23F1 /* AppStore-Mac */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_ARC = YES; + }; + name = "AppStore-Mac"; + }; DA4425D41557BED40052177D /* Debug-Mac */ = { isa = XCBuildConfiguration; buildSettings = { @@ -2108,6 +2212,16 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + DA25091519513C1500AC23F1 /* Build configuration list for PBXNativeTarget "RMBlurredView" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DA25091619513C1500AC23F1 /* Debug-Mac */, + DA25091719513C1500AC23F1 /* AdHoc-Mac */, + DA25091819513C1500AC23F1 /* AppStore-Mac */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = "AdHoc-Mac"; + }; DA4425D31557BED40052177D /* Build configuration list for PBXNativeTarget "UbiquityStoreManager" */ = { isa = XCConfigurationList; buildConfigurations = (