2
0

Remove dialog & add import/export

[REMOVED]   Mac password Dialog.
[ADDED]     Mac sites import / export.
[FIXED]     Copying of hidden passwords.
This commit is contained in:
Maarten Billemont 2014-06-28 23:45:06 -04:00
parent 34d8dc375f
commit 971538d6b5
21 changed files with 340 additions and 1316 deletions

2
External/Pearl vendored

@ -1 +1 @@
Subproject commit a3f33c5f0cbafd6910ae55fb313764c18b48ad69
Subproject commit 59dc05c9cca43504bad026dc4633dbba34b4cc43

View File

@ -11,13 +11,13 @@
#import "UbiquityStoreManager.h"
#import "MPFixable.h"
typedef enum {
typedef NS_ENUM( NSUInteger, MPImportResult ) {
MPImportResultSuccess,
MPImportResultCancelled,
MPImportResultInvalidPassword,
MPImportResultMalformedInput,
MPImportResultInternalError,
} MPImportResult;
};
@interface MPAppDelegate_Shared(Store)<UbiquityStoreManagerDelegate>

View File

@ -784,7 +784,7 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
[export appendFormat:@"%@ %8ld %8s %20s\t%@\n",
[[NSDateFormatter rfc3339DateFormatter] stringFromDate:lastUsed], (long)uses,
[PearlString( @"%lu:%lu", (long)type, (unsigned long)version ) UTF8String], [name UTF8String], content
[strf( @"%lu:%lu", (long)type, (unsigned long)version ) UTF8String], [name UTF8String], content
? content: @""];
}

View File

@ -1,33 +0,0 @@
/**
* 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 <lhunath@lyndir.com>
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
*/
//
// MPElementCollectionView.h
// MPElementCollectionView
//
// Created by lhunath on 2/11/2014.
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
//
#import <Foundation/Foundation.h>
@class MPElementModel;
@interface MPElementCollectionView : NSCollectionViewItem
@property (nonatomic) MPElementModel *representedObject;
@property (nonatomic) BOOL counterHidden;
@property (nonatomic) BOOL updateContentHidden;
- (IBAction)toggleType:(id)sender;
- (IBAction)updateLoginName:(id)sender;
- (IBAction)updateContent:(id)sender;
- (IBAction)delete:(id)sender;
@end

View File

@ -1,225 +0,0 @@
/**
* 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 <lhunath@lyndir.com>
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
*/
//
// MPElementCollectionView.h
// MPElementCollectionView
//
// Created by lhunath on 2/11/2014.
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
//
#import "MPElementCollectionView.h"
#import "MPElementModel.h"
#import "MPMacAppDelegate.h"
#import "MPAppDelegate_Store.h"
#import "MPPasswordDialogController.h"
#define MPAlertChangeType @"MPAlertChangeType"
#define MPAlertChangeLogin @"MPAlertChangeLogin"
#define MPAlertChangeContent @"MPAlertChangeContent"
#define MPAlertDeleteSite @"MPAlertDeleteSite"
@implementation MPElementCollectionView {
}
@dynamic representedObject;
- (id)initWithCoder:(NSCoder *)coder {
if (!(self = [super initWithCoder:coder]))
return nil;
[self addObserver:self forKeyPath:@"representedObject" options:0 context:nil];
return self;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.counterHidden = !(MPElementTypeClassGenerated & self.representedObject.type);
self.updateContentHidden = !(MPElementTypeClassStored & self.representedObject.type);
}];
}
- (void)dealloc {
[self removeObserver:self forKeyPath:@"representedObject"];
}
- (IBAction)toggleType:(id)sender {
id<MPAlgorithm> algorithm = self.representedObject.algorithm;
NSString *previousType = [algorithm nameOfType:[algorithm previousType:self.representedObject.type]];
NSString *nextType = [algorithm nameOfType:[algorithm nextType:self.representedObject.type]];
[[NSAlert alertWithMessageText:@"Change Password Type"
defaultButton:nextType alternateButton:@"Cancel" otherButton:previousType
informativeTextWithFormat:@"Changing the password type for this site will cause the password to change.\n"
@"You will need to update your account with the new password.\n\n"
@"Changing back to the old type will restore your current password."]
beginSheetModalForWindow:self.view.window modalDelegate:self
didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:MPAlertChangeType];
}
- (IBAction)updateLoginName:(id)sender {
NSAlert *alert = [NSAlert alertWithMessageText:@"Update Login Name"
defaultButton:@"Update" alternateButton:@"Cancel" otherButton:nil
informativeTextWithFormat:@"Enter the login name for %@:", self.representedObject.siteName];
NSTextField *passwordField = [[NSTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )];
[alert setAccessoryView:passwordField];
[alert layout];
[passwordField becomeFirstResponder];
[alert beginSheetModalForWindow:self.view.window modalDelegate:self
didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:MPAlertChangeLogin];
}
- (IBAction)updateContent:(id)sender {
NSAlert *alert = [NSAlert alertWithMessageText:@"Update Password"
defaultButton:@"Update" alternateButton:@"Cancel" otherButton:nil
informativeTextWithFormat:@"Enter the new password for %@:", self.representedObject.siteName];
NSSecureTextField *passwordField = [[NSSecureTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )];
[alert setAccessoryView:passwordField];
[alert layout];
[passwordField becomeFirstResponder];
[alert beginSheetModalForWindow:self.view.window modalDelegate:self
didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:MPAlertChangeContent];
}
- (IBAction)delete:(id)sender {
NSAlert *alert = [NSAlert alertWithMessageText:@"Delete Site"
defaultButton:@"Delete" alternateButton:@"Cancel" otherButton:nil
informativeTextWithFormat:@"Are you sure you want to delete the site: %@?", self.representedObject.siteName];
[alert beginSheetModalForWindow:self.view.window modalDelegate:self
didEndSelector:@selector( alertDidEnd:returnCode:contextInfo: ) contextInfo:MPAlertDeleteSite];
}
- (void)alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo {
if (contextInfo == MPAlertChangeType) {
switch (returnCode) {
case NSAlertDefaultReturn: {
// "Next type" button.
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPElementEntity *element = [self.representedObject entityInContext:context];
element = [[MPMacAppDelegate get] changeElement:element saveInContext:context
toType:[element.algorithm nextType:element.type]];
self.representedObject = [[MPElementModel alloc] initWithEntity:element];
}];
break;
}
case NSAlertAlternateReturn: {
// "Cancel" button.
break;
}
case NSAlertOtherReturn: {
// "Previous type" button.
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPElementEntity *element = [self.representedObject entityInContext:context];
element = [[MPMacAppDelegate get] changeElement:element saveInContext:context
toType:[element.algorithm previousType:element.type]];
self.representedObject = [[MPElementModel alloc] initWithEntity:element];
}];
break;
}
default:
break;
}
return;
}
if (contextInfo == MPAlertChangeLogin) {
switch (returnCode) {
case NSAlertDefaultReturn: {
// "Update" button.
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPElementEntity *element = [self.representedObject entityInContext:context];
element.loginName = [(NSTextField *)alert.accessoryView stringValue];
[context saveToStore];
self.representedObject = [[MPElementModel alloc] initWithEntity:element];
}];
break;
}
case NSAlertAlternateReturn: {
// "Cancel" button.
break;
}
default:
break;
}
return;
}
if (contextInfo == MPAlertChangeContent) {
switch (returnCode) {
case NSAlertDefaultReturn: {
// "Update" button.
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPElementEntity *element = [self.representedObject entityInContext:context];
[element.algorithm saveContent:[(NSSecureTextField *)alert.accessoryView stringValue]
toElement:element usingKey:[MPMacAppDelegate get].key];
[context saveToStore];
self.representedObject = [[MPElementModel alloc] initWithEntity:element];
}];
break;
}
case NSAlertAlternateReturn: {
// "Cancel" button.
break;
}
default:
break;
}
return;
}
if (contextInfo == MPAlertDeleteSite) {
switch (returnCode) {
case NSAlertDefaultReturn: {
// "Delete" button.
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPElementEntity *element = [self.representedObject entityInContext:context];
[context deleteObject:element];
[context saveToStore];
[((MPPasswordDialogController *)self.collectionView.window.windowController) updateElements];
}];
break;
}
case NSAlertAlternateReturn: {
// "Cancel" button.
break;
}
default:
break;
}
return;
}
}
@end

View File

@ -25,6 +25,7 @@
@property (nonatomic) MPElementType type;
@property (nonatomic) NSString *typeName;
@property (nonatomic) NSString *content;
@property (nonatomic) NSString *contentDisplay;
@property (nonatomic) NSString *loginName;
@property (nonatomic) NSNumber *uses;
@property (nonatomic) NSUInteger counter;

View File

@ -111,13 +111,21 @@
- (void)updateContent:(MPElementEntity *)entity {
[entity resolveContentUsingKey:[MPAppDelegate_Shared get].key result:^(NSString *result) {
if ([[MPConfig get].hidePasswords boolValue] && !([NSEvent modifierFlags] & NSAlternateKeyMask))
result = [result stringByReplacingMatchesOfExpression:
[NSRegularExpression regularExpressionWithPattern:@"." options:0 error:nil]
withTemplate:@"●"];
static NSRegularExpression *re_anyChar;
static dispatch_once_t once = 0;
dispatch_once( &once, ^{
re_anyChar = [NSRegularExpression regularExpressionWithPattern:@"." options:0 error:nil];
} );
PearlMainQueue( ^{ self.content = result; } );
[entity resolveContentUsingKey:[MPAppDelegate_Shared get].key result:^(NSString *result) {
NSString *displayResult = result;
if ([[MPConfig get].hidePasswords boolValue] && !([NSEvent modifierFlags] & NSAlternateKeyMask))
displayResult = [displayResult stringByReplacingMatchesOfExpression:re_anyChar withTemplate:@"●"];
PearlMainQueue( ^{
self.content = result;
self.contentDisplay = displayResult;
} );
}];
}

View File

@ -26,8 +26,6 @@
@property(nonatomic, weak) IBOutlet NSMenuItem *createUserItem;
@property(nonatomic, weak) IBOutlet NSMenuItem *deleteUserItem;
@property(nonatomic, weak) IBOutlet NSMenuItem *usersItem;
@property(nonatomic, weak) IBOutlet NSMenuItem *dialogStyleRegular;
@property(nonatomic, weak) IBOutlet NSMenuItem *dialogStyleHUD;
@property(nonatomic, weak) IBOutlet NSButton *openAtLoginButton;
@property(nonatomic, weak) IBOutlet NSButton *enableCloudButton;

View File

@ -37,7 +37,7 @@ static EventHotKeyID MPLockHotKey = { .signature = 'lock', .id = 1 };
+ (void)initialize {
static dispatch_once_t initialize;
static dispatch_once_t initialize = 0;
dispatch_once( &initialize, ^{
[MPMacConfig get];
@ -71,7 +71,6 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
PearlProfiler *profiler = [PearlProfiler profilerForTask:@"applicationDidFinishLaunching"];
// Setup delegates and listeners.
[MPConfig get].delegate = self;
__weak id weakSelf = self;
@ -90,7 +89,6 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
[weakSelf updateMenuItems];
} );
} forKeyPath:@"storeManager.cloudAvailable" options:0 context:nil];
[profiler finishJob:@"observers"];
// Status item.
self.statusView = [[RHStatusItemView alloc] initWithStatusBarItem:
@ -99,41 +97,24 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
self.statusView.menu = self.statusMenu;
self.statusView.target = self;
self.statusView.action = @selector( showMenu );
[profiler finishJob:@"statusView"];
[[NSNotificationCenter defaultCenter] addObserverForName:USMStoreDidChangeNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock:
^(NSNotification *note) {
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
[self updateUsers];
}];
[[NSNotificationCenter defaultCenter] addObserverForName:USMStoreDidImportChangesNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock:
^(NSNotification *note) {
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
[self updateUsers];
}];
[[NSNotificationCenter defaultCenter] addObserverForName:MPCheckConfigNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock:
^(NSNotification *note) {
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
NSString *key = note.object;
if (!key || [key isEqualToString:NSStringFromSelector( @selector( hidePasswords ) )])
self.hidePasswordsItem.state = [[MPConfig get].hidePasswords boolValue]? NSOnState: NSOffState;
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];
}
}
}];
[profiler finishJob:@"notificationCenter"];
[self updateUsers];
[profiler finishJob:@"updateUsers"];
// Global hotkey.
EventHotKeyRef hotKeyRef;
@ -148,7 +129,6 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
status = RegisterEventHotKey( 35 /* p */, controlKey + optionKey + cmdKey, MPLockHotKey, GetApplicationEventTarget(), 0, &hotKeyRef );
if (status != noErr)
err( @"Error registering 'lock' hotkey: %i", (int)status );
[profiler finishJob:@"hotKey"];
// Initial display.
if ([[MPMacConfig get].firstRun boolValue]) {
@ -156,7 +136,6 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
[self.initialWindow.window setLevel:NSFloatingWindowLevel];
[self.initialWindow showWindow:self];
}
[profiler finishJob:@"initial display"];
}
- (void)applicationWillResignActive:(NSNotification *)notification {
@ -230,12 +209,113 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
[self signOutAnimated:NO];
NSError *error = nil;
NSManagedObjectContext *context = [MPMacAppDelegate managedObjectContextForMainThreadIfReady];
self.activeUser = (MPUserEntity *)[context existingObjectWithID:[item representedObject] error:&error];
NSManagedObjectContext *mainContext = [MPMacAppDelegate managedObjectContextForMainThreadIfReady];
self.activeUser = [MPUserEntity existingObjectWithID:[item representedObject] inContext:mainContext];
}
if (error)
err( @"While looking up selected user: %@", error );
- (IBAction)exportSitesSecure:(id)sender {
[self exportSitesAndRevealPasswords:NO];
}
- (IBAction)exportSitesReveal:(id)sender {
[self exportSitesAndRevealPasswords:YES];
}
- (IBAction)importSites:(id)sender {
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
openPanel.allowsMultipleSelection = NO;
openPanel.canChooseDirectories = NO;
openPanel.title = @"Master Password";
openPanel.message = @"Locate the Master Password export file to import.";
openPanel.prompt = @"Import";
openPanel.directoryURL = [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask].firstObject;
openPanel.allowedFileTypes = @[ @"mpsites" ];
[NSApp activateIgnoringOtherApps:YES];
if ([openPanel runModal] == NSFileHandlingPanelCancelButton)
return;
NSURL *url = openPanel.URL;
PearlNotMainQueue( ^{
NSError *error;
NSURLResponse *response;
NSData *importedSitesData = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:url]
returningResponse:&response error:&error];
if (error)
err( @"While reading imported sites from %@: %@", url, error );
if (!importedSitesData)
return;
NSString *importedSitesString = [[NSString alloc] initWithData:importedSitesData encoding:NSUTF8StringEncoding];
MPImportResult result = [self importSites:importedSitesString askImportPassword:^NSString *(NSString *userName) {
__block NSString *masterPassword = nil;
PearlMainQueueWait( ^{
NSAlert *alert = [NSAlert new];
[alert addButtonWithTitle:@"Unlock"];
[alert addButtonWithTitle:@"Cancel"];
alert.messageText = @"Import File's Master Password";
alert.informativeText = strf( @"%@'s export was done using a different master password.\n"
@"Enter that master password to unlock the exported data.", userName );
alert.accessoryView = [[NSSecureTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )];
[alert layout];
if ([alert runModal] == NSAlertFirstButtonReturn)
masterPassword = ((NSTextField *)alert.accessoryView).stringValue;
} );
return masterPassword;
} askUserPassword:^NSString *(NSString *userName, NSUInteger importCount, NSUInteger deleteCount) {
__block NSString *masterPassword = nil;
PearlMainQueueWait( ^{
NSAlert *alert = [NSAlert new];
[alert addButtonWithTitle:@"Import"];
[alert addButtonWithTitle:@"Cancel"];
alert.messageText = strf( @"Master Password for\n%@", userName );
alert.informativeText = strf( @"Imports %lu sites, overwriting %lu.",
(unsigned long)importCount, (unsigned long)deleteCount );
alert.accessoryView = [[NSSecureTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )];
[alert layout];
if ([alert runModal] == NSAlertFirstButtonReturn)
masterPassword = ((NSTextField *)alert.accessoryView).stringValue;
} );
return masterPassword;
}];
PearlMainQueue( ^{
switch (result) {
case MPImportResultSuccess: {
[self updateUsers];
NSAlert *alert = [NSAlert new];
alert.messageText = @"Successfully imported sites.";
[alert runModal];
break;
}
case MPImportResultCancelled:
break;
case MPImportResultInternalError:
[NSAlert alertWithError:[NSError errorWithDomain:MPErrorDomain code:0 userInfo:@{
NSLocalizedDescriptionKey : @"Import failed because of an internal error."
}]];
break;
case MPImportResultMalformedInput:
[NSAlert alertWithError:[NSError errorWithDomain:MPErrorDomain code:0 userInfo:@{
NSLocalizedDescriptionKey : @"The import doesn't look like a Master Password export."
}]];
break;
case MPImportResultInvalidPassword:
[NSAlert alertWithError:[NSError errorWithDomain:MPErrorDomain code:0 userInfo:@{
NSLocalizedDescriptionKey : @"Incorrect master password for the import sites."
}]];
break;
}
} );
} );
}
- (IBAction)togglePreference:(id)sender {
@ -269,10 +349,6 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
[context saveToStore];
}];
}
if (sender == self.dialogStyleRegular)
[MPMacConfig get].dialogStyleHUD = @NO;
if (sender == self.dialogStyleHUD)
[MPMacConfig get].dialogStyleHUD = @YES;
[MPMacConfig flush];
}
@ -381,23 +457,81 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
// If no user, can't activate.
if (![self activeUserForMainThread]) {
NSAlert *alert = [NSAlert new];
alert.messageText = @"No User Selected";
alert.informativeText = @"Begin by selecting or creating your user from the status menu (●●●|) next to the clock.";
[alert runModal];
[self showPopup:nil];
[[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];
return;
}
// Don't show window if we weren't already running (ie. if we haven't been activated before).
PearlProfiler *profiler = [PearlProfiler profilerForTask:@"passwordWindow"];
if (!self.passwordWindow)
self.passwordWindow = [[MPPasswordWindowController alloc] initWithWindowNibName:@"MPPasswordWindowController"];
[profiler finishJob:@"init"];
[self.passwordWindow showWindow:self];
[profiler finishJob:@"show"];
}
#pragma mark - Private
- (void)exportSitesAndRevealPasswords:(BOOL)revealPasswords {
MPUserEntity *mainActiveUser = [self activeUserForMainThread];
if (!mainActiveUser) {
NSAlert *alert = [NSAlert new];
alert.messageText = @"No User Selected";
alert.informativeText = @"To export your sites, first select the user whose sites to export.";
[alert runModal];
[self showPopup:nil];
return;
}
if (!self.key) {
NSAlert *alert = [NSAlert new];
alert.messageText = @"User Locked";
alert.informativeText = @"To export your sites, first unlock your user by opening Master Password.";
[alert runModal];
[self showPopup:nil];
return;
}
NSDateFormatter *exportDateFormatter = [NSDateFormatter new];
[exportDateFormatter setDateFormat:@"yyyy'-'MM'-'dd"];
NSSavePanel *savePanel = [NSSavePanel savePanel];
savePanel.title = @"Master Password";
savePanel.message = @"Pick a location for the export Master Password's sites.";
if (revealPasswords)
savePanel.message = strf( @"%@\nWARNING: Your passwords will be visible. Make sure to always keep the file in a secure location.",
savePanel.message );
savePanel.prompt = @"Export";
savePanel.directoryURL = [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask].firstObject;
savePanel.nameFieldStringValue = strf( @"%@ (%@).mpsites", mainActiveUser.name,
[exportDateFormatter stringFromDate:[NSDate date]] );
savePanel.allowedFileTypes = @[ @"mpsites" ];
[NSApp activateIgnoringOtherApps:YES];
if ([savePanel runModal] == NSFileHandlingPanelCancelButton)
return;
NSError *coordinateError = nil;
NSString *exportedSites = [self exportSitesRevealPasswords:revealPasswords];
[[[NSFileCoordinator alloc] initWithFilePresenter:nil] coordinateWritingItemAtURL:savePanel.URL options:0 error:&coordinateError
byAccessor:^(NSURL *newURL) {
NSError *writeError = nil;
if (![exportedSites writeToURL:newURL atomically:NO encoding:NSUTF8StringEncoding error:&writeError])
PearlMainQueue( ^{
[[NSAlert alertWithError:writeError] runModal];
} );
}];
if (coordinateError)
PearlMainQueue( ^{
[[NSAlert alertWithError:coordinateError] runModal];
} );
}
- (void)updateUsers {
[[[self.usersItem submenu] itemArray] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
@ -405,8 +539,8 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
[[self.usersItem submenu] removeItem:obj];
}];
NSManagedObjectContext *context = [MPMacAppDelegate managedObjectContextForMainThreadIfReady];
if (!context) {
NSManagedObjectContext *mainContext = [MPMacAppDelegate managedObjectContextForMainThreadIfReady];
if (!mainContext) {
self.createUserItem.title = @"New User (Not ready)";
self.createUserItem.enabled = NO;
self.createUserItem.toolTip = @"Please wait until the app is fully loaded.";
@ -418,20 +552,20 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
return;
}
MPUserEntity *activeUser = [self activeUserInContext:context];
MPUserEntity *mainActiveUser = [self activeUserInContext:mainContext];
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.";
self.deleteUserItem.title = mainActiveUser? @"Delete User": @"Delete User (None Selected)";
self.deleteUserItem.enabled = mainActiveUser != nil;
self.deleteUserItem.toolTip = mainActiveUser? 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];
NSArray *users = [mainContext executeFetchRequest:fetchRequest error:&error];
if (!users)
err( @"Failed to load users: %@", error );
@ -449,10 +583,10 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
[userItem setRepresentedObject:[user objectID]];
[[self.usersItem submenu] addItem:userItem];
if (!activeUser && [user.name isEqualToString:[MPMacConfig get].usedUserName])
[super setActiveUser:activeUser = user];
if (!mainActiveUser && [user.name isEqualToString:[MPMacConfig get].usedUserName])
[super setActiveUser:mainActiveUser = user];
if ([activeUser isEqual:user]) {
if ([mainActiveUser isEqual:user]) {
userItem.state = NSOnState;
self.usersItem.state = NSOffState;
}

View File

@ -11,6 +11,5 @@
@interface MPMacConfig : MPConfig
@property(nonatomic, retain) NSString *usedUserName;
@property(nonatomic, retain) NSNumber *dialogStyleHUD;
@end

View File

@ -9,7 +9,6 @@
@implementation MPMacConfig
@dynamic usedUserName;
@dynamic dialogStyleHUD;
- (id)init {
@ -18,7 +17,6 @@
[self.defaults registerDefaults:@{
NSStringFromSelector( @selector(iTunesID) ) : @"510296984",
NSStringFromSelector( @selector(dialogStyleHUD) ) : @NO,
}];
return self;

View File

@ -1,23 +0,0 @@
//
// MPPasswordDialogController.h
// MasterPassword-Mac
//
// Created by Maarten Billemont on 04/03/12.
// Copyright (c) 2012 Lyndir. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@class MPElementModel;
@interface MPPasswordDialogController : NSWindowController<NSTextFieldDelegate, NSCollectionViewDelegate>
@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

View File

@ -1,478 +0,0 @@
//
// 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.siteName );
else
notification.subtitle = selectedElement.siteName;
[[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

View File

@ -1,380 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="5056" systemVersion="13D65" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment defaultVersion="1080" identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="5056"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="MPPasswordDialogController">
<connections>
<outlet property="contentContainer" destination="143" id="214"/>
<outlet property="siteCollectionView" destination="pr9-BO-vQV" id="Bnh-WQ-RaO"/>
<outlet property="siteField" destination="182" id="224"/>
<outlet property="userLabel" destination="216" id="223"/>
<outlet property="window" destination="22" id="40"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application"/>
<window title="Master Password" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="22" customClass="NSPanel">
<windowStyleMask key="styleMask" titled="YES" closable="YES" resizable="YES" texturedBackground="YES"/>
<rect key="contentRect" x="600" y="530" width="480" height="320"/>
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="900"/>
<value key="minSize" type="size" width="480" height="320"/>
<value key="maxSize" type="size" width="480" height="320"/>
<view key="contentView" wantsLayer="YES" id="23">
<rect key="frame" x="0.0" y="0.0" width="480" height="320"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="143">
<rect key="frame" x="0.0" y="0.0" width="480" height="320"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="182">
<rect key="frame" x="140" y="256" width="200" height="22"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<constraints>
<constraint firstAttribute="width" constant="200" id="258"/>
</constraints>
<shadow key="shadow" blurRadius="2">
<color key="color" name="controlShadowColor" catalog="System" colorSpace="catalog"/>
</shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" alignment="center" placeholderString="Site name" usesSingleLineMode="YES" bezelStyle="round" id="185">
<font key="font" metaFont="cellTitle"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<outlet property="delegate" destination="-2" id="188"/>
</connections>
</textField>
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="216">
<rect key="frame" x="146" y="286" width="188" height="14"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" alignment="center" title="Maarten Billemont's password for:" usesSingleLineMode="YES" id="217">
<font key="font" metaFont="palette"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<scrollView autohidesScrollers="YES" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" hasVerticalScroller="NO" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="tjI-mV-s8H">
<rect key="frame" x="0.0" y="0.0" width="480" height="248"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<clipView key="contentView" id="mfh-QT-ClZ">
<rect key="frame" x="1" y="1" width="478" height="246"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<collectionView selectable="YES" maxNumberOfColumns="1" id="pr9-BO-vQV">
<rect key="frame" x="0.0" y="0.0" width="478" height="246"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="primaryBackgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<color key="secondaryBackgroundColor" name="controlAlternatingRowColor" catalog="System" colorSpace="catalog"/>
<connections>
<binding destination="jTN-Q9-Ajn" name="content" keyPath="arrangedObjects" id="dtF-fs-Fpe"/>
<binding destination="-2" name="selectionIndexes" keyPath="elementSelectionIndexes" previousBinding="dtF-fs-Fpe" id="UFy-9F-18H"/>
<outlet property="delegate" destination="-2" id="4cD-GP-imR"/>
<outlet property="itemPrototype" destination="QBZ-sO-HQn" id="QAi-HN-YUc"/>
</connections>
</collectionView>
</subviews>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</clipView>
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="6vP-Im-BRg">
<rect key="frame" x="-100" y="-100" width="233" height="15"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="vs6-1G-hvM">
<rect key="frame" x="-100" y="-100" width="15" height="143"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
<button horizontalHuggingPriority="750" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="tLu-3k-QiL">
<rect key="frame" x="449" y="288" width="25" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="help" bezelStyle="helpButton" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="51o-8V-9eq">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</button>
</subviews>
<constraints>
<constraint firstItem="216" firstAttribute="top" secondItem="143" secondAttribute="top" constant="20" symbolic="YES" id="218"/>
<constraint firstItem="182" firstAttribute="top" secondItem="216" secondAttribute="bottom" constant="8" symbolic="YES" id="221"/>
<constraint firstAttribute="bottom" secondItem="tjI-mV-s8H" secondAttribute="bottom" id="6y8-tJ-1sX"/>
<constraint firstItem="tLu-3k-QiL" firstAttribute="top" secondItem="143" secondAttribute="top" constant="8" id="9h7-dN-MqZ"/>
<constraint firstAttribute="trailing" secondItem="tLu-3k-QiL" secondAttribute="trailing" constant="8" id="UQN-Ab-fIg"/>
<constraint firstAttribute="centerX" secondItem="182" secondAttribute="centerX" id="W6t-BO-Njn"/>
<constraint firstAttribute="trailing" secondItem="tjI-mV-s8H" secondAttribute="trailing" id="aKe-a9-Br8"/>
<constraint firstItem="tjI-mV-s8H" firstAttribute="leading" secondItem="143" secondAttribute="leading" id="d8F-JD-EhR"/>
<constraint firstItem="tjI-mV-s8H" firstAttribute="top" secondItem="182" secondAttribute="bottom" constant="8" symbolic="YES" id="izG-uH-9BF"/>
<constraint firstAttribute="centerX" secondItem="216" secondAttribute="centerX" id="mkv-5E-dy5"/>
</constraints>
</customView>
</subviews>
<constraints>
<constraint firstItem="143" firstAttribute="leading" secondItem="23" secondAttribute="leading" id="145"/>
<constraint firstItem="143" firstAttribute="bottom" secondItem="23" secondAttribute="bottom" id="147"/>
<constraint firstItem="143" firstAttribute="trailing" secondItem="23" secondAttribute="trailing" id="148"/>
<constraint firstItem="143" firstAttribute="top" secondItem="23" secondAttribute="top" id="150"/>
</constraints>
</view>
<connections>
<outlet property="delegate" destination="-2" id="39"/>
</connections>
</window>
<collectionViewItem id="QBZ-sO-HQn" customClass="MPElementCollectionView">
<connections>
<outlet property="view" destination="bhu-Ky-PQq" id="jZh-jC-6bL"/>
</connections>
</collectionViewItem>
<arrayController objectClassName="MPElementModel" id="jTN-Q9-Ajn">
<connections>
<binding destination="-2" name="contentArray" keyPath="self.elements" id="8Uz-14-YG0"/>
</connections>
</arrayController>
<box autoresizesSubviews="NO" wantsLayer="YES" boxType="custom" borderType="none" titlePosition="noTitle" id="bhu-Ky-PQq">
<rect key="frame" x="0.0" y="0.0" width="960" height="61"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<view key="contentView">
<rect key="frame" x="0.0" y="0.0" width="960" height="61"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="xwJ-Pu-glP">
<rect key="frame" x="6" y="35" width="60" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<shadow key="shadow" blurRadius="1">
<size key="offset" width="0.0" height="1"/>
<color key="color" white="0.0" alpha="0.59999999999999998" colorSpace="calibratedWhite"/>
</shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="left" title="apple.com" usesSingleLineMode="YES" id="ymH-M0-M5d">
<font key="font" size="12" name="Exo2.0-Light"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<binding destination="QBZ-sO-HQn" name="value" keyPath="representedObject.siteName" id="4as-ow-WbD"/>
</connections>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Cn5-nt-e6X">
<rect key="frame" x="362" y="35" width="111" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<shadow key="shadow" blurRadius="1">
<size key="offset" width="0.0" height="1"/>
<color key="color" white="0.0" alpha="0.59999999999999998" colorSpace="calibratedWhite"/>
</shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="lhunath@lyndir.com" usesSingleLineMode="YES" id="Px4-pS-kwG">
<font key="font" size="12" name="Exo2.0-Light"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<binding destination="QBZ-sO-HQn" name="value" keyPath="representedObject.loginName" id="C2s-1d-p2H">
<dictionary key="options">
<bool key="NSContinuouslyUpdatesValue" value="YES"/>
</dictionary>
</binding>
</connections>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="a1n-Sf-Mw6">
<rect key="frame" x="6" y="8" width="206" height="38"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<shadow key="shadow" blurRadius="1">
<size key="offset" width="0.0" height="1"/>
<color key="color" white="0.0" alpha="0.59999999999999998" colorSpace="calibratedWhite"/>
</shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="left" title="RutuTutnTeni4," id="7jb-Fa-xX3">
<font key="font" size="24" name="SourceCodePro-Light"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<binding destination="QBZ-sO-HQn" name="value" keyPath="representedObject.content" id="41c-PF-OeH">
<dictionary key="options">
<bool key="NSContinuouslyUpdatesValue" value="YES"/>
</dictionary>
</binding>
</connections>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="mp4-r1-7Xg">
<rect key="frame" x="440" y="8" width="33" height="38"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<shadow key="shadow" blurRadius="1">
<size key="offset" width="0.0" height="1"/>
<color key="color" white="0.0" alpha="0.59999999999999998" colorSpace="calibratedWhite"/>
</shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="12" id="Vb9-nO-cWY">
<font key="font" size="24" name="SourceCodePro-Light"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<binding destination="QBZ-sO-HQn" name="value" keyPath="representedObject.uses" id="gSt-Nd-lVa">
<dictionary key="options">
<bool key="NSContinuouslyUpdatesValue" value="YES"/>
</dictionary>
</binding>
</connections>
</textField>
<box autoresizesSubviews="NO" horizontalHuggingPriority="750" title="Box" boxType="separator" titlePosition="noTitle" translatesAutoresizingMaskIntoConstraints="NO" id="tsT-Xh-Nxb">
<rect key="frame" x="477" y="0.0" width="5" height="61"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<color key="borderColor" white="0.0" alpha="0.41999999999999998" colorSpace="calibratedWhite"/>
<color key="fillColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<font key="titleFont" metaFont="system"/>
</box>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="vEl-aL-Qd4">
<rect key="frame" x="791" y="27" width="167" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="lhunath@lyndir.com" bezelStyle="rounded" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="sl3-bH-Hh2">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="updateLoginName:" target="QBZ-sO-HQn" id="pJI-k8-LRl"/>
<binding destination="QBZ-sO-HQn" name="title" keyPath="representedObject.loginName" id="uaP-Y1-bz6">
<dictionary key="options">
<string key="NSNullPlaceholder">Click to set login name</string>
</dictionary>
</binding>
</connections>
</button>
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="d2a-pG-7CW">
<rect key="frame" x="528" y="31" width="127" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="Long Password" bezelStyle="rounded" alignment="center" lineBreakMode="truncatingTail" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" selectedItem="7Bj-S7-WSd" id="RZo-c7-kqA">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" title="OtherViews" id="mQN-XZ-YKz">
<items>
<menuItem title="Long Password" state="on" id="7Bj-S7-WSd"/>
<menuItem title="Item 2" id="WUd-69-IBV"/>
<menuItem title="Item 3" id="pgH-yZ-G0J"/>
</items>
</menu>
</popUpButtonCell>
<connections>
<binding destination="QBZ-sO-HQn" name="contentValues" keyPath="representedObject.typeNames" id="eyo-f9-MwY"/>
<binding destination="QBZ-sO-HQn" name="selectedIndex" keyPath="representedObject.typeIndex" previousBinding="eyo-f9-MwY" id="dpI-HX-GpX"/>
</connections>
</popUpButton>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="npg-mh-AfY">
<rect key="frame" x="486" y="36" width="38" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Type:" id="U2E-DE-2j4">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="hqw-ZP-4c1">
<rect key="frame" x="486" y="11" width="59" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Number:" id="CDU-Ah-QDj">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<binding destination="QBZ-sO-HQn" name="hidden" keyPath="counterHidden" id="xlz-gk-Gwc"/>
</connections>
</textField>
<stepper horizontalHuggingPriority="750" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="JPX-xQ-VKZ">
<rect key="frame" x="565" y="6" width="19" height="27"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<stepperCell key="cell" alignment="left" minValue="1" maxValue="100" doubleValue="1" autorepeat="NO" id="ELG-fy-nVF"/>
<connections>
<binding destination="QBZ-sO-HQn" name="hidden" keyPath="counterHidden" id="3FO-ks-R7U"/>
<binding destination="QBZ-sO-HQn" name="value" keyPath="representedObject.counter" id="awJ-Vg-RUn"/>
</connections>
</stepper>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="kZI-sG-dEn">
<rect key="frame" x="549" y="11" width="13" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="1" id="EUB-gJ-7Db">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<binding destination="QBZ-sO-HQn" name="hidden" keyPath="counterHidden" id="ZFX-79-eAY"/>
<binding destination="QBZ-sO-HQn" name="value" keyPath="representedObject.counter" id="20y-tn-B5k"/>
</connections>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="qOt-Rp-G9T">
<rect key="frame" x="877" y="2" width="81" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Delete" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="QCU-VC-W0u">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="delete:" target="QBZ-sO-HQn" id="mLJ-JY-5fz"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="5YU-4W-4gz">
<rect key="frame" x="727" y="2" width="150" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Change Password" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="NQY-Oo-Eid">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="updateContent:" target="QBZ-sO-HQn" id="J8f-vx-10A"/>
<binding destination="QBZ-sO-HQn" name="hidden" keyPath="updateContentHidden" id="UwG-p5-kH4"/>
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="bzg-90-kmh">
<rect key="frame" x="748" y="36" width="43" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Login:" id="uxE-6c-RjC">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
</view>
<constraints>
<constraint firstItem="hqw-ZP-4c1" firstAttribute="centerY" secondItem="qOt-Rp-G9T" secondAttribute="centerY" id="6xp-tI-BkX"/>
<constraint firstItem="npg-mh-AfY" firstAttribute="leading" secondItem="tsT-Xh-Nxb" secondAttribute="trailing" constant="8" id="7kp-OB-3BI"/>
<constraint firstItem="kZI-sG-dEn" firstAttribute="leading" secondItem="hqw-ZP-4c1" secondAttribute="trailing" constant="8" symbolic="YES" id="8qK-Et-c1F"/>
<constraint firstItem="d2a-pG-7CW" firstAttribute="leading" secondItem="npg-mh-AfY" secondAttribute="trailing" constant="8" symbolic="YES" id="F6X-FD-kEX"/>
<constraint firstItem="Cn5-nt-e6X" firstAttribute="top" secondItem="bhu-Ky-PQq" secondAttribute="top" constant="8" id="FWS-tY-KkM"/>
<constraint firstItem="hqw-ZP-4c1" firstAttribute="top" secondItem="npg-mh-AfY" secondAttribute="bottom" constant="8" symbolic="YES" id="GuG-5I-quA"/>
<constraint firstItem="hqw-ZP-4c1" firstAttribute="leading" secondItem="tsT-Xh-Nxb" secondAttribute="trailing" constant="8" id="Hzf-XZ-auZ"/>
<constraint firstItem="tsT-Xh-Nxb" firstAttribute="leading" secondItem="Cn5-nt-e6X" secondAttribute="trailing" constant="8" id="LjD-10-tVU"/>
<constraint firstItem="tsT-Xh-Nxb" firstAttribute="top" secondItem="bhu-Ky-PQq" secondAttribute="top" id="ODH-Nx-QdY"/>
<constraint firstItem="qOt-Rp-G9T" firstAttribute="centerY" secondItem="5YU-4W-4gz" secondAttribute="centerY" id="PdB-X5-eqF"/>
<constraint firstItem="xwJ-Pu-glP" firstAttribute="leading" secondItem="bhu-Ky-PQq" secondAttribute="leading" constant="8" id="QAM-jm-t0y"/>
<constraint firstAttribute="centerY" secondItem="tsT-Xh-Nxb" secondAttribute="centerY" id="Rpi-lc-dZ1"/>
<constraint firstItem="tsT-Xh-Nxb" firstAttribute="leading" secondItem="mp4-r1-7Xg" secondAttribute="trailing" constant="8" id="Xfc-Jq-2Rb"/>
<constraint firstAttribute="trailing" secondItem="vEl-aL-Qd4" secondAttribute="trailing" constant="8" id="ZZu-AS-C07"/>
<constraint firstAttribute="bottom" secondItem="tsT-Xh-Nxb" secondAttribute="bottom" id="ds1-7i-B7I"/>
<constraint firstItem="a1n-Sf-Mw6" firstAttribute="leading" secondItem="bhu-Ky-PQq" secondAttribute="leading" constant="8" id="e3g-2s-yee"/>
<constraint firstItem="npg-mh-AfY" firstAttribute="centerY" secondItem="vEl-aL-Qd4" secondAttribute="centerY" id="ftr-2Z-9J0"/>
<constraint firstItem="npg-mh-AfY" firstAttribute="centerY" secondItem="d2a-pG-7CW" secondAttribute="centerY" id="g9a-r0-EOo"/>
<constraint firstAttribute="bottom" secondItem="mp4-r1-7Xg" secondAttribute="bottom" constant="8" id="jCW-5H-6pT"/>
<constraint firstAttribute="centerX" secondItem="tsT-Xh-Nxb" secondAttribute="centerX" id="kx6-g0-jUW"/>
<constraint firstAttribute="bottom" secondItem="a1n-Sf-Mw6" secondAttribute="bottom" constant="8" id="mWW-rt-kl2"/>
<constraint firstItem="npg-mh-AfY" firstAttribute="top" secondItem="bhu-Ky-PQq" secondAttribute="top" constant="8" id="n7Z-EF-Dcm"/>
<constraint firstItem="qOt-Rp-G9T" firstAttribute="leading" secondItem="5YU-4W-4gz" secondAttribute="trailing" constant="12" symbolic="YES" id="uWm-hE-itO"/>
<constraint firstAttribute="trailing" secondItem="qOt-Rp-G9T" secondAttribute="trailing" constant="8" id="x6J-1l-rU4"/>
<constraint firstItem="hqw-ZP-4c1" firstAttribute="centerY" secondItem="kZI-sG-dEn" secondAttribute="centerY" id="xZ7-Mc-9dq"/>
<constraint firstItem="vEl-aL-Qd4" firstAttribute="leading" secondItem="bzg-90-kmh" secondAttribute="trailing" constant="8" symbolic="YES" id="z7h-om-WAV"/>
<constraint firstItem="xwJ-Pu-glP" firstAttribute="top" secondItem="bhu-Ky-PQq" secondAttribute="top" constant="8" id="z8k-ot-NjU"/>
<constraint firstItem="bzg-90-kmh" firstAttribute="centerY" secondItem="vEl-aL-Qd4" secondAttribute="centerY" id="zbz-Qg-uHO"/>
<constraint firstItem="JPX-xQ-VKZ" firstAttribute="centerY" secondItem="kZI-sG-dEn" secondAttribute="centerY" id="zcO-M3-gyr"/>
<constraint firstItem="JPX-xQ-VKZ" firstAttribute="leading" secondItem="kZI-sG-dEn" secondAttribute="trailing" constant="8" symbolic="YES" id="zvB-Vk-IL2"/>
</constraints>
<color key="borderColor" white="0.0" alpha="0.41999999999999998" colorSpace="calibratedWhite"/>
<color key="fillColor" name="selectedControlColor" catalog="System" colorSpace="catalog"/>
<connections>
<binding destination="QBZ-sO-HQn" name="transparent" keyPath="selected" id="Tpz-Rp-lA7">
<dictionary key="options">
<string key="NSValueTransformerName">NSNegateBoolean</string>
</dictionary>
</binding>
</connections>
</box>
</objects>
</document>

View File

@ -1,8 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="5056" systemVersion="13D65" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6154.17" systemVersion="13D65" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment defaultVersion="1080" identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="5056"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6154.17"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="MPPasswordWindowController">
@ -25,7 +24,7 @@
<windowCollectionBehavior key="collectionBehavior" transient="YES" ignoresCycle="YES" fullScreenAuxiliary="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="0.0" y="0.0" width="640" height="530"/>
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="900"/>
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="878"/>
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
<rect key="frame" x="0.0" y="0.0" width="640" height="530"/>
<autoresizingMask key="autoresizingMask"/>
@ -66,14 +65,13 @@
<outlet property="delegate" destination="-2" id="egd-Ny-IEz"/>
</connections>
</secureTextField>
<scrollView focusRingType="none" borderType="none" autohidesScrollers="YES" horizontalLineScroll="31" horizontalPageScroll="10" verticalLineScroll="31" verticalPageScroll="10" hasHorizontalScroller="NO" horizontalScrollElasticity="none" translatesAutoresizingMaskIntoConstraints="NO" id="Bme-XK-MMc" userLabel="Sites Table">
<scrollView focusRingType="none" borderType="none" autohidesScrollers="YES" horizontalLineScroll="35" horizontalPageScroll="10" verticalLineScroll="35" verticalPageScroll="10" hasHorizontalScroller="NO" horizontalScrollElasticity="none" translatesAutoresizingMaskIntoConstraints="NO" id="Bme-XK-MMc" userLabel="Sites Table">
<rect key="frame" x="64" y="80" width="512" height="132"/>
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="e11-59-xSS">
<rect key="frame" x="0.0" y="0.0" width="512" height="132"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="none" selectionHighlightStyle="sourceList" columnReordering="NO" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" rowHeight="29" rowSizeStyle="automatic" viewBased="YES" floatsGroupRows="NO" id="xvJ-5c-vDp">
<rect key="frame" x="0.0" y="0.0" width="515" height="132"/>
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="none" selectionHighlightStyle="sourceList" columnReordering="NO" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" rowHeight="33" rowSizeStyle="automatic" viewBased="YES" floatsGroupRows="NO" id="xvJ-5c-vDp">
<autoresizingMask key="autoresizingMask"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="calibratedRGB"/>
@ -93,11 +91,11 @@
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES"/>
<prototypeCellViews>
<tableCellView id="xQb-o5-M5U">
<rect key="frame" x="1" y="1" width="512" height="29"/>
<rect key="frame" x="1" y="1" width="512" height="33"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="ydd-Rv-tra">
<rect key="frame" x="-2" y="0.0" width="516" height="29"/>
<rect key="frame" x="-2" y="2" width="516" height="29"/>
<shadow key="shadow" blurRadius="1">
<size key="offset" width="0.0" height="1"/>
<color key="color" white="0.0" alpha="0.80000000000000004" colorSpace="calibratedWhite"/>
@ -118,11 +116,20 @@
</textField>
</subviews>
<constraints>
<constraint firstAttribute="bottom" secondItem="ydd-Rv-tra" secondAttribute="bottom" id="5YP-zp-i6Y"/>
<constraint firstAttribute="bottom" secondItem="ydd-Rv-tra" secondAttribute="bottom" constant="2" id="5YP-zp-i6Y"/>
<constraint firstAttribute="trailing" secondItem="ydd-Rv-tra" secondAttribute="trailing" id="GcM-cT-kQi"/>
<constraint firstItem="ydd-Rv-tra" firstAttribute="leading" secondItem="xQb-o5-M5U" secondAttribute="leading" id="Wot-L7-qhr"/>
<constraint firstItem="ydd-Rv-tra" firstAttribute="top" secondItem="xQb-o5-M5U" secondAttribute="top" id="eZT-Sd-wW5"/>
<constraint firstItem="ydd-Rv-tra" firstAttribute="top" secondItem="xQb-o5-M5U" secondAttribute="top" constant="2" id="eZT-Sd-wW5"/>
</constraints>
<backgroundFilters>
<ciFilter name="CIGloom">
<configuration>
<null key="inputImage"/>
<real key="inputIntensity" value="1"/>
<real key="inputRadius" value="10"/>
</configuration>
</ciFilter>
</backgroundFilters>
<connections>
<outlet property="textField" destination="ydd-Rv-tra" id="lMV-D4-Ilq"/>
</connections>
@ -324,11 +331,9 @@
</button>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="NGk-Io-Buc">
<rect key="frame" x="20" y="323" width="600" height="163"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="XUV-zU-Y9c">
<rect key="frame" x="96" y="45" width="408" height="73"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<shadow key="shadow" blurRadius="1">
<size key="offset" width="0.0" height="1"/>
<color key="color" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
@ -353,7 +358,7 @@
<string key="NSValueTransformerName">NSNegateBoolean</string>
</dictionary>
</binding>
<binding destination="mcS-ik-b0n" name="value" keyPath="selection.content" id="iXY-0g-zZW"/>
<binding destination="mcS-ik-b0n" name="value" keyPath="selection.contentDisplay" id="djg-i5-pwt"/>
</connections>
</textField>
</subviews>
@ -364,7 +369,6 @@
</customView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="rhm-sC-xFS">
<rect key="frame" x="80" y="220" width="481" height="15"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<shadow key="shadow" blurRadius="1">
<size key="offset" width="0.0" height="1"/>
<color key="color" white="0.0" alpha="0.70000000000000007" colorSpace="calibratedWhite"/>
@ -384,7 +388,6 @@
</textField>
<button translatesAutoresizingMaskIntoConstraints="NO" id="Aue-Zx-6Mf">
<rect key="frame" x="588" y="478" width="32" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="square" bezelStyle="shadowlessSquare" image="icon_gear" imagePosition="only" alignment="center" imageScaling="proportionallyDown" inset="2" id="i8r-9N-vcQ">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
@ -397,7 +400,6 @@
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="qal-PP-YtO">
<rect key="frame" x="399" y="1" width="23" height="15"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<shadow key="shadow" blurRadius="1">
<size key="offset" width="0.0" height="1"/>
<color key="color" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
@ -408,6 +410,11 @@
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<binding destination="-2" name="hidden" keyPath="alternatePressed" id="pgW-yB-Zx3">
<dictionary key="options">
<string key="NSValueTransformerName">NSNegateBoolean</string>
</dictionary>
</binding>
<binding destination="mcS-ik-b0n" name="hidden2" keyPath="canRemove" previousBinding="pgW-yB-Zx3" id="XHu-Py-efU">
<dictionary key="options">
<integer key="NSMultipleValuesPlaceholder" value="-1"/>
@ -417,26 +424,25 @@
<string key="NSValueTransformerName">NSNegateBoolean</string>
</dictionary>
</binding>
<binding destination="-2" name="hidden" keyPath="alternatePressed" id="pgW-yB-Zx3">
<dictionary key="options">
<string key="NSValueTransformerName">NSNegateBoolean</string>
</dictionary>
</binding>
</connections>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="9b3-wy-KBb">
<rect key="frame" x="309" y="2" width="22" height="14"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<shadow key="shadow" blurRadius="1">
<size key="offset" width="0.0" height="1"/>
<color key="color" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
</shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="⌘T" id="HFM-Bk-akx">
<font key="font" size="11" name="LucidaGrande"/>
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="highlightColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<binding destination="-2" name="hidden" keyPath="alternatePressed" id="CG8-3H-uiD">
<dictionary key="options">
<string key="NSValueTransformerName">NSNegateBoolean</string>
</dictionary>
</binding>
<binding destination="mcS-ik-b0n" name="hidden2" keyPath="canRemove" previousBinding="CG8-3H-uiD" id="n0n-ZB-u5H">
<dictionary key="options">
<integer key="NSMultipleValuesPlaceholder" value="-1"/>
@ -446,16 +452,10 @@
<string key="NSValueTransformerName">NSNegateBoolean</string>
</dictionary>
</binding>
<binding destination="-2" name="hidden" keyPath="alternatePressed" id="CG8-3H-uiD">
<dictionary key="options">
<string key="NSValueTransformerName">NSNegateBoolean</string>
</dictionary>
</binding>
</connections>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Ido-NQ-3MY">
<rect key="frame" x="197" y="1" width="22" height="15"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<shadow key="shadow" blurRadius="1">
<size key="offset" width="0.0" height="1"/>
<color key="color" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
@ -466,6 +466,11 @@
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<binding destination="-2" name="hidden" keyPath="alternatePressed" id="b3W-u1-1A2">
<dictionary key="options">
<string key="NSValueTransformerName">NSNegateBoolean</string>
</dictionary>
</binding>
<binding destination="mcS-ik-b0n" name="hidden2" keyPath="canRemove" previousBinding="b3W-u1-1A2" id="gTg-Go-ELg">
<dictionary key="options">
<integer key="NSMultipleValuesPlaceholder" value="-1"/>
@ -475,26 +480,25 @@
<string key="NSValueTransformerName">NSNegateBoolean</string>
</dictionary>
</binding>
<binding destination="-2" name="hidden" keyPath="alternatePressed" id="b3W-u1-1A2">
<dictionary key="options">
<string key="NSValueTransformerName">NSNegateBoolean</string>
</dictionary>
</binding>
</connections>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="uol-dE-I8H">
<rect key="frame" x="309" y="70" width="22" height="14"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<shadow key="shadow" blurRadius="1">
<size key="offset" width="0.0" height="1"/>
<color key="color" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
</shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="⌘P" id="MyN-x6-dMk">
<font key="font" size="11" name="LucidaGrande"/>
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="highlightColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<binding destination="-2" name="hidden" keyPath="alternatePressed" id="MLo-fO-mx5">
<dictionary key="options">
<string key="NSValueTransformerName">NSNegateBoolean</string>
</dictionary>
</binding>
<binding destination="mcS-ik-b0n" name="hidden2" keyPath="canRemove" previousBinding="MLo-fO-mx5" id="vpQ-kw-9ZS">
<dictionary key="options">
<integer key="NSMultipleValuesPlaceholder" value="-1"/>
@ -513,22 +517,16 @@
<string key="NSValueTransformerName">NSNegateBoolean</string>
</dictionary>
</binding>
<binding destination="-2" name="hidden" keyPath="alternatePressed" id="MLo-fO-mx5">
<dictionary key="options">
<string key="NSValueTransformerName">NSNegateBoolean</string>
</dictionary>
</binding>
</connections>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gAU-xs-aae">
<rect key="frame" x="595" y="460" width="19" height="14"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<shadow key="shadow" blurRadius="1">
<size key="offset" width="0.0" height="1"/>
<color key="color" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
</shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="⌘," id="Xm1-qb-6EP">
<font key="font" size="11" name="LucidaGrande"/>
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="highlightColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
@ -606,7 +604,6 @@
<subviews>
<matrix verticalHuggingPriority="750" allowsEmptySelection="NO" autorecalculatesCellSize="YES" translatesAutoresizingMaskIntoConstraints="NO" id="3fr-Fd-pxx">
<rect key="frame" x="18" y="14" width="348" height="158"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
<size key="cellSize" width="179" height="18"/>
<size key="intercellSpacing" width="4" height="2"/>

View File

@ -8,6 +8,29 @@
<string>en</string>
<key>CFBundleDisplayName</key>
<string>Master Password</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>mpsites</string>
</array>
<key>CFBundleTypeIconFile</key>
<string></string>
<key>CFBundleTypeIconFiles</key>
<array/>
<key>CFBundleTypeName</key>
<string>Master Password sites</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSHandlerRank</key>
<string>Owner</string>
<key>LSItemContentTypes</key>
<array>
<string>com.lyndir.lhunath.MasterPassword.sites</string>
</array>
</dict>
</array>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
@ -24,6 +47,19 @@
<string>[auto]</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>com.lyndir.lhunath.MasterPassword</string>
<key>CFBundleURLSchemes</key>
<array>
<string>com.lyndir.lhunath.MasterPassword</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>[auto]</string>
<key>LSApplicationCategoryType</key>
@ -38,5 +74,27 @@
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeDescription</key>
<string>Master Password sites</string>
<key>UTTypeIconFile</key>
<string></string>
<key>UTTypeIdentifier</key>
<string>com.lyndir.lhunath.MasterPassword.sites</string>
<key>UTTypeSize320IconFile</key>
<string></string>
<key>UTTypeSize64IconFile</key>
<string></string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>mpsites</string>
</array>
</dict>
</dict>
</array>
</dict>
</plist>

View File

@ -13,7 +13,6 @@
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 */; };
@ -64,10 +63,7 @@
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 /* 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 */; };
DA5E5D0B1724A667003798D8 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA5E5CC41724A667003798D8 /* MainMenu.xib */; };
DA5E5D0C1724A667003798D8 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CC61724A667003798D8 /* main.m */; };
@ -312,9 +308,7 @@
93D39240B5143E01F0B75E96 /* MPElementModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementModel.h; sourceTree = "<group>"; };
93D392C3918763B3B72CF366 /* MPPasswordWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordWindowController.h; sourceTree = "<group>"; };
93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Indexing.h"; sourceTree = "<group>"; };
93D394495528B10D1B61A2C3 /* MPElementCollectionView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementCollectionView.m; sourceTree = "<group>"; };
93D394EEFF5BF555A55AF361 /* PearlProfiler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PearlProfiler.h; path = ../../../External/Pearl/Pearl/PearlProfiler.h; sourceTree = "<group>"; };
93D3960D320FF8A072B092E3 /* MPElementCollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementCollectionView.h; sourceTree = "<group>"; };
93D396D04E57792A54D437AC /* NSArray+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Indexing.h"; sourceTree = "<group>"; };
93D3977484534E99F9BA579D /* MPPasswordWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordWindow.h; sourceTree = "<group>"; };
93D39A57A7823DE98A0FF83C /* MPPasswordWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordWindowController.m; sourceTree = "<group>"; };
@ -393,13 +387,9 @@
DA5E5CB41724A667003798D8 /* MPMacAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPMacAppDelegate.m; sourceTree = "<group>"; };
DA5E5CB51724A667003798D8 /* MPMacConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPMacConfig.h; sourceTree = "<group>"; };
DA5E5CB61724A667003798D8 /* MPMacConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPMacConfig.m; sourceTree = "<group>"; };
DA5E5CB71724A667003798D8 /* MPPasswordDialogController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordDialogController.h; sourceTree = "<group>"; };
DA5E5CB81724A667003798D8 /* MPPasswordDialogController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordDialogController.m; sourceTree = "<group>"; };
DA5E5CB91724A667003798D8 /* MPPasswordDialogController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MPPasswordDialogController.xib; sourceTree = "<group>"; };
DA5E5CBA1724A667003798D8 /* MasterPassword-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "MasterPassword-Info.plist"; sourceTree = "<group>"; };
DA5E5CBE1724A667003798D8 /* MasterPassword-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MasterPassword-Prefix.pch"; sourceTree = "<group>"; };
DA5E5CBF1724A667003798D8 /* MasterPassword.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = MasterPassword.entitlements; sourceTree = "<group>"; };
DA5E5CC11724A667003798D8 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = en; path = en.lproj/Credits.rtf; sourceTree = "<group>"; };
DA5E5CC31724A667003798D8 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
DA5E5CC51724A667003798D8 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainMenu.xib; sourceTree = "<group>"; };
DA5E5CC61724A667003798D8 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
@ -1206,19 +1196,13 @@
DA5E5CB41724A667003798D8 /* MPMacAppDelegate.m */,
DA5E5CB51724A667003798D8 /* MPMacConfig.h */,
DA5E5CB61724A667003798D8 /* MPMacConfig.m */,
DA5E5CB71724A667003798D8 /* MPPasswordDialogController.h */,
DA5E5CB81724A667003798D8 /* MPPasswordDialogController.m */,
DA5E5CB91724A667003798D8 /* MPPasswordDialogController.xib */,
DA5E5CBA1724A667003798D8 /* MasterPassword-Info.plist */,
DA5E5CBE1724A667003798D8 /* MasterPassword-Prefix.pch */,
DA5E5CBF1724A667003798D8 /* MasterPassword.entitlements */,
DA5E5CC01724A667003798D8 /* Credits.rtf */,
DA5E5CC21724A667003798D8 /* InfoPlist.strings */,
DA5E5CC41724A667003798D8 /* MainMenu.xib */,
DA5E5CC61724A667003798D8 /* main.m */,
DA0933C91747A56A00DE1CEF /* MPInitialWindow.xib */,
93D394495528B10D1B61A2C3 /* MPElementCollectionView.m */,
93D3960D320FF8A072B092E3 /* MPElementCollectionView.h */,
93D39E73BF5CBF8E5B005CD3 /* MPElementModel.m */,
93D39240B5143E01F0B75E96 /* MPElementModel.h */,
DA2508F019511D3600AC23F1 /* MPPasswordWindowController.xib */,
@ -2396,9 +2380,7 @@
DACA296F1705DF81002C6C22 /* Crashlytics.plist in Resources */,
DACA29731705E1A8002C6C22 /* ciphers.plist in Resources */,
DACA29741705E1A8002C6C22 /* dictionary.lst in Resources */,
DA5E5D061724A667003798D8 /* MPPasswordDialogController.xib in Resources */,
DA5E5D081724A667003798D8 /* MasterPassword.entitlements in Resources */,
DA5E5D091724A667003798D8 /* Credits.rtf in Resources */,
DA5E5D0A1724A667003798D8 /* InfoPlist.strings in Resources */,
DA5E5D0B1724A667003798D8 /* MainMenu.xib in Resources */,
DA5E5D551724F9C8003798D8 /* MasterPassword.iconset in Resources */,
@ -2479,10 +2461,8 @@
DA5E5D021724A667003798D8 /* MPUserEntity.m in Sources */,
DA5E5D031724A667003798D8 /* MPMacAppDelegate.m in Sources */,
DA5E5D041724A667003798D8 /* MPMacConfig.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 */,
@ -2560,14 +2540,6 @@
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
DA5E5CC01724A667003798D8 /* Credits.rtf */ = {
isa = PBXVariantGroup;
children = (
DA5E5CC11724A667003798D8 /* en */,
);
name = Credits.rtf;
sourceTree = "<group>";
};
DA5E5CC21724A667003798D8 /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
@ -2798,7 +2770,6 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_OBJC_ARC = YES;
CODE_SIGN_ENTITLEMENTS = MasterPassword.entitlements;
CODE_SIGN_IDENTITY = "Mac Developer";
COMBINE_HIDPI_IMAGES = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@ -2818,7 +2789,6 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_OBJC_ARC = YES;
CODE_SIGN_ENTITLEMENTS = MasterPassword.entitlements;
CODE_SIGN_IDENTITY = "Mac Developer";
COMBINE_HIDPI_IMAGES = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",

View File

@ -15,5 +15,7 @@
<array>
<string>HL3Q45LX9N.com.lyndir.lhunath.MasterPassword.Mac</string>
</array>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
</dict>
</plist>

View File

@ -1,29 +0,0 @@
{\rtf0\ansi{\fonttbl\f0\fswiss Helvetica;}
{\colortbl;\red255\green255\blue255;}
\paperw9840\paperh8400
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural
\f0\b\fs24 \cf0 Engineering:
\b0 \
Some people\
\
\b Human Interface Design:
\b0 \
Some other people\
\
\b Testing:
\b0 \
Hopefully not nobody\
\
\b Documentation:
\b0 \
Whoever\
\
\b With special thanks to:
\b0 \
Mom\
}

View File

@ -17,8 +17,6 @@
<connections>
<outlet property="createUserItem" destination="757" id="763"/>
<outlet property="deleteUserItem" destination="ZgZ-p2-463" id="smU-PF-mKA"/>
<outlet property="dialogStyleHUD" destination="768" id="771"/>
<outlet property="dialogStyleRegular" destination="767" id="772"/>
<outlet property="hidePasswordsItem" destination="9G7-17-PzY" id="qPX-VT-jVx"/>
<outlet property="lockItem" destination="720" id="726"/>
<outlet property="openAtLoginItem" destination="785" id="788"/>
@ -97,7 +95,7 @@
<action selector="togglePreference:" target="494" id="xgz-pN-csV"/>
</connections>
</menuItem>
<menuItem title="Don't show site passwords (hold alt ⌥ to reveal them)." enabled="NO" id="HnK-hQ-cM9">
<menuItem title="Remember the password while the application is running." enabled="NO" id="HnK-hQ-cM9">
<attributedString key="attributedTitle">
<fragment content="Remember the password while the application is running.">
<attributes>
@ -142,25 +140,53 @@
</attributedString>
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem title="Password Dialog Style" id="765">
<menuItem title="Export Sites" id="r1P-hr-mh5">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Password Dialog Style" id="766">
<menu key="submenu" title="Export Sites" id="fnk-gK-yCi">
<items>
<menuItem title="Regular" id="767">
<menuItem title="Secure Export" id="06i-og-eLt">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="togglePreference:" target="494" id="773"/>
<action selector="exportSitesSecure:" target="494" id="LVH-es-imA"/>
</connections>
</menuItem>
<menuItem title="HUD" tag="1" id="768">
<menuItem title="Your passwords are not visible." enabled="NO" id="ybY-P3-eao">
<attributedString key="attributedTitle">
<fragment content="Your passwords are not visible.">
<attributes>
<font key="NSFont" size="12" name="Helvetica"/>
<paragraphStyle key="NSParagraphStyle" alignment="natural" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
</attributes>
</fragment>
</attributedString>
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem title="Reveal Passwords" id="fMG-TT-bTn">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="togglePreference:" target="494" id="774"/>
<action selector="exportSitesReveal:" target="494" id="1IW-VT-Oeu"/>
</connections>
</menuItem>
<menuItem title="Keep this file secure or delete it when you're done with it!" enabled="NO" id="cQu-oR-SUa">
<attributedString key="attributedTitle">
<fragment content="Keep this file secure or delete it when you're done with it!">
<attributes>
<font key="NSFont" size="12" name="Helvetica"/>
<paragraphStyle key="NSParagraphStyle" alignment="natural" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
</attributes>
</fragment>
</attributedString>
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Import Sites..." id="EFK-zt-EvJ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="importSites:" target="494" id="CNv-4j-036"/>
</connections>
</menuItem>
<menuItem title="Advanced" id="776">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Advanced" id="777">

View File

@ -182,7 +182,7 @@
dispatch_group_enter( importPasswordGroup );
dispatch_async( dispatch_get_main_queue(), ^{
[PearlAlert showAlertWithTitle:@"Import File's Master Password"
message:PearlString( @"%@'s export was done using a different master password.\n"
message:strf( @"%@'s export was done using a different master password.\n"
@"Enter that master password to unlock the exported data.", userName )
viewStyle:UIAlertViewStyleSecureTextInput
initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
@ -405,6 +405,7 @@
[PearlInfoPlist get].CFBundleShortVersionString,
[PearlInfoPlist get].CFBundleVersion );
NSString *exportedSites = [self exportSitesRevealPasswords:revealPasswords];
NSDateFormatter *exportDateFormatter = [NSDateFormatter new];
[exportDateFormatter setDateFormat:@"yyyy'-'MM'-'dd"];