2
0

Collection-view for elements and swipe to modify.

[ADDED]     NSCollectionView for navigating between elements.
[ADDED]     Mac: Functional buttons for changing type, loginName and counter.
This commit is contained in:
Maarten Billemont 2014-02-19 00:59:01 -05:00
parent c48bed5ebd
commit 775a6fd4ea
14 changed files with 563 additions and 201 deletions

View File

@ -37,6 +37,8 @@
- (NSString *)shortNameOfType:(MPElementType)type;
- (NSString *)classNameOfType:(MPElementType)type;
- (Class)classOfType:(MPElementType)type;
- (MPElementType)nextType:(MPElementType)type;
- (MPElementType)previousType:(MPElementType)type;
- (NSString *)generateContentNamed:(NSString *)name ofType:(MPElementType)type withCounter:(NSUInteger)counter usingKey:(MPKey *)key;
- (NSString *)storedContentForElement:(MPElementStoredEntity *)element usingKey:(MPKey *)key;

View File

@ -127,7 +127,7 @@
return @"Device Private Password";
}
Throw(@"Type not supported: %d", type);
Throw(@"Type not supported: %lu", type);
}
- (NSString *)shortNameOfType:(MPElementType)type {
@ -161,7 +161,7 @@
return @"Device";
}
Throw(@"Type not supported: %d", type);
Throw(@"Type not supported: %lu", type);
}
- (NSString *)classNameOfType:(MPElementType)type {
@ -200,7 +200,43 @@
return [MPElementStoredEntity class];
}
Throw(@"Type not supported: %d", type);
Throw(@"Type not supported: %lu", type);
}
- (MPElementType)nextType:(MPElementType)type {
if (!type)
Throw(@"No type given.");
switch (type) {
case MPElementTypeGeneratedMaximum:
return MPElementTypeStoredDevicePrivate;
case MPElementTypeGeneratedLong:
return MPElementTypeGeneratedMaximum;
case MPElementTypeGeneratedMedium:
return MPElementTypeGeneratedLong;
case MPElementTypeGeneratedBasic:
return MPElementTypeGeneratedMedium;
case MPElementTypeGeneratedShort:
return MPElementTypeGeneratedBasic;
case MPElementTypeGeneratedPIN:
return MPElementTypeGeneratedShort;
case MPElementTypeStoredPersonal:
return MPElementTypeGeneratedPIN;
case MPElementTypeStoredDevicePrivate:
return MPElementTypeStoredPersonal;
}
Throw(@"Type not supported: %lu", type);
}
- (MPElementType)previousType:(MPElementType)type {
MPElementType previousType = type, nextType = type;
while ((nextType = [self nextType:nextType]) != type)
previousType = nextType;
return previousType;
}
- (NSString *)generateContentNamed:(NSString *)name ofType:(MPElementType)type withCounter:(NSUInteger)counter usingKey:(MPKey *)key {
@ -264,13 +300,13 @@
case MPElementTypeGeneratedBasic:
case MPElementTypeGeneratedShort:
case MPElementTypeGeneratedPIN: {
NSAssert(NO, @"Cannot save content to element with generated type %d.", element.type);
NSAssert(NO, @"Cannot save content to element with generated type %lu.", element.type);
break;
}
case MPElementTypeStoredPersonal: {
NSAssert([element isKindOfClass:[MPElementStoredEntity class]],
@"Element with stored type %d is not an MPElementStoredEntity, but a %@.", element.type, [element class]);
@"Element with stored type %lu is not an MPElementStoredEntity, but a %@.", element.type, [element class]);
NSData *encryptedContent = [[clearContent dataUsingEncoding:NSUTF8StringEncoding]
encryptWithSymmetricKey:[elementKey subKeyOfLength:PearlCryptKeySize].keyData padding:YES];
@ -279,7 +315,7 @@
}
case MPElementTypeStoredDevicePrivate: {
NSAssert([element isKindOfClass:[MPElementStoredEntity class]],
@"Element with stored type %d is not an MPElementStoredEntity, but a %@.", element.type, [element class]);
@"Element with stored type %lu is not an MPElementStoredEntity, but a %@.", element.type, [element class]);
NSData *encryptedContent = [[clearContent dataUsingEncoding:NSUTF8StringEncoding]
encryptWithSymmetricKey:[elementKey subKeyOfLength:PearlCryptKeySize].keyData padding:YES];
@ -324,7 +360,7 @@
case MPElementTypeGeneratedShort:
case MPElementTypeGeneratedPIN: {
NSAssert([element isKindOfClass:[MPElementGeneratedEntity class]],
@"Element with generated type %d is not an MPElementGeneratedEntity, but a %@.", element.type, [element class]);
@"Element with generated type %lu is not an MPElementGeneratedEntity, but a %@.", element.type, [element class]);
NSString *name = element.name;
MPElementType type = element.type;
@ -346,7 +382,7 @@
case MPElementTypeStoredPersonal: {
NSAssert([element isKindOfClass:[MPElementStoredEntity class]],
@"Element with stored type %d is not an MPElementStoredEntity, but a %@.", element.type, [element class]);
@"Element with stored type %lu is not an MPElementStoredEntity, but a %@.", element.type, [element class]);
NSData *encryptedContent = ((MPElementStoredEntity *)element).contentObject;
@ -358,7 +394,7 @@
}
case MPElementTypeStoredDevicePrivate: {
NSAssert([element isKindOfClass:[MPElementStoredEntity class]],
@"Element with stored type %d is not an MPElementStoredEntity, but a %@.", element.type, [element class]);
@"Element with stored type %lu is not an MPElementStoredEntity, but a %@.", element.type, [element class]);
NSDictionary *elementQuery = [self queryForDevicePrivateElementNamed:element.name];
NSData *encryptedContent = [PearlKeyChain dataOfItemForQuery:elementQuery];
@ -387,7 +423,7 @@
case MPElementTypeStoredPersonal: {
NSAssert([element isKindOfClass:[MPElementStoredEntity class]],
@"Element with stored type %d is not an MPElementStoredEntity, but a %@.", element.type, [element class]);
@"Element with stored type %lu is not an MPElementStoredEntity, but a %@.", element.type, [element class]);
if ([importKey.keyID isEqualToData:elementKey.keyID])
((MPElementStoredEntity *)element).contentObject = [protectedContent decodeBase64];
@ -445,7 +481,7 @@
case MPElementTypeStoredPersonal: {
NSAssert([element isKindOfClass:[MPElementStoredEntity class]],
@"Element with stored type %d is not an MPElementStoredEntity, but a %@.", element.type, [element class]);
@"Element with stored type %lu is not an MPElementStoredEntity, but a %@.", element.type, [element class]);
result = [((MPElementStoredEntity *)element).contentObject encodeBase64];
break;
}

View File

@ -409,6 +409,8 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
newElement.loginName = element.loginName;
[context deleteObject:element];
// TODO: Dodgy... we're not saving consistently here.
// Either we should save regardless and change the method signature to saveInContext: or not save at all.
[context saveToStore];
NSError *error;
@ -699,7 +701,7 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
[export appendFormat:@"%@ %8ld %8s %20s\t%@\n",
[[NSDateFormatter rfc3339DateFormatter] stringFromDate:lastUsed], (long)uses,
[PearlString( @"%u:%lu", type, (unsigned long)version ) UTF8String], [name UTF8String], content
[PearlString( @"%lu:%lu", type, (unsigned long)version ) UTF8String], [name UTF8String], content
? content: @""];
}

View File

@ -53,6 +53,8 @@
aType = [self.user defaultType];
if (!aType || aType == (MPElementType)NSNotFound)
aType = MPElementTypeGeneratedLong;
if (![self isKindOfClass:[self.algorithm classOfType:aType]])
Throw(@"This object's class does not support the type: %lu", aType);
self.type_ = @(aType);
}
@ -125,7 +127,7 @@
- (NSString *)debugDescription {
return PearlString( @"{%@: name=%@, user=%@, type=%d, uses=%ld, lastUsed=%@, version=%ld, loginName=%@, requiresExplicitMigration=%d}",
return PearlString( @"{%@: name=%@, user=%@, type=%lu, uses=%ld, lastUsed=%@, version=%ld, loginName=%@, requiresExplicitMigration=%d}",
NSStringFromClass( [self class] ), self.name, self.user.name, self.type, (long)self.uses, self.lastUsed, (long)self.version,
self.loginName, self.requiresExplicitMigration );
}

View File

@ -8,27 +8,27 @@
#import "MPKey.h"
typedef enum {
typedef NS_ENUM(NSUInteger, MPElementContentType) {
MPElementContentTypePassword,
MPElementContentTypeNote,
MPElementContentTypePicture,
} MPElementContentType;
};
typedef enum {
typedef NS_ENUM(NSUInteger, MPElementTypeClass) {
/** Generate the password. */
MPElementTypeClassGenerated = 1 << 4,
/** Store the password. */
MPElementTypeClassStored = 1 << 5,
} MPElementTypeClass;
};
typedef enum {
typedef NS_ENUM(NSUInteger, MPElementFeature) {
/** Export the key-protected content data. */
MPElementFeatureExportContent = 1 << 10,
/** Never export content. */
MPElementFeatureDevicePrivate = 1 << 11,
} MPElementFeature;
};
typedef enum {
typedef NS_ENUM(NSUInteger, MPElementType) {
MPElementTypeGeneratedMaximum = 0x0 | MPElementTypeClassGenerated | 0x0,
MPElementTypeGeneratedLong = 0x1 | MPElementTypeClassGenerated | 0x0,
MPElementTypeGeneratedMedium = 0x2 | MPElementTypeClassGenerated | 0x0,
@ -38,7 +38,7 @@ typedef enum {
MPElementTypeStoredPersonal = 0x0 | MPElementTypeClassStored | MPElementFeatureExportContent,
MPElementTypeStoredDevicePrivate = 0x1 | MPElementTypeClassStored | MPElementFeatureDevicePrivate,
} MPElementType;
};
#define MPErrorDomain @"MPErrorDomain"

View File

@ -17,9 +17,17 @@
//
#import <Foundation/Foundation.h>
@class MPElementModel;
@interface MPElementCollectionView : NSCollectionViewItem
@property(nonatomic, weak) IBOutlet NSTextField *siteField;
@property(nonatomic, weak) IBOutlet NSTextField *contentField;
@property (nonatomic) MPElementModel *representedObject;
@property (nonatomic) NSString *typeTitle;
@property (nonatomic) NSString *loginNameTitle;
@property (nonatomic) NSString *counterTitle;
- (IBAction)toggleType:(id)sender;
- (IBAction)setLoginName:(id)sender;
- (IBAction)incrementCounter:(id)sender;
@end

View File

@ -17,7 +17,237 @@
//
#import "MPElementCollectionView.h"
#import "MPElementModel.h"
#import "MPMacAppDelegate.h"
#import "MPAppDelegate_Store.h"
#define MPAlertChangeType @"MPAlertChangeType"
#define MPAlertChangeLogin @"MPAlertChangeLogin"
#define MPAlertChangeCounter @"MPAlertChangeCounter"
#define MPAlertChangeContent @"MPAlertChangeContent"
@implementation MPElementCollectionView {
id _representedObjectObserver;
}
@dynamic representedObject;
- (id)initWithCoder:(NSCoder *)coder {
if (!(self = [super initWithCoder:coder]))
return nil;
__weak MPElementCollectionView *wSelf = self;
_representedObjectObserver = [self addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
dispatch_async( dispatch_get_main_queue(), ^{
dbg(@"updating login name of %@ to: %@", wSelf.representedObject.site, wSelf.representedObject.loginName);
wSelf.typeTitle = PearlString( @"Type:\n%@", wSelf.representedObject.typeName );
wSelf.loginNameTitle = PearlString( @"Login Name:\n%@", wSelf.representedObject.loginName );
if (wSelf.representedObject.type & MPElementTypeClassGenerated)
wSelf.counterTitle = PearlString( @"Number:\n%@", wSelf.representedObject.counter );
else if (wSelf.representedObject.type & MPElementTypeClassStored)
wSelf.counterTitle = PearlString( @"Update Password" );
} );
} forKeyPath:@"representedObject" options:0 context:nil];
return self;
}
- (void)dealloc {
if (_representedObjectObserver)
[self removeObserver:_representedObjectObserver 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)setLoginName:(id)sender {
NSAlert *alert = [NSAlert alertWithMessageText:@"Update Login Name"
defaultButton:@"Update" alternateButton:@"Cancel" otherButton:nil
informativeTextWithFormat:@"Enter the login name for %@:", self.representedObject.site];
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)incrementCounter:(id)sender {
if (self.representedObject.type & MPElementTypeClassGenerated) {
[[NSAlert alertWithMessageText:@"Change Password Number"
defaultButton:@"New Password" alternateButton:@"Cancel" otherButton:@"Initial Password"
informativeTextWithFormat:@"Increasing the password number gives you a new password for the site.\n"
@"You will need to update your account with the new password.\n\n"
@"Changing back to the initial password will reset the password number to 1."]
beginSheetModalForWindow:self.view.window modalDelegate:self
didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:MPAlertChangeCounter];
}
else if (self.representedObject.type & MPElementTypeClassStored) {
NSAlert *alert = [NSAlert alertWithMessageText:@"Update Password"
defaultButton:@"Update" alternateButton:@"Cancel" otherButton:nil
informativeTextWithFormat:@"Enter the new password for %@:", self.representedObject.site];
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];
}
}
- (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 inContext:context
toType:[element.algorithm nextType:element.type]];
[context saveToStore];
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 inContext:context
toType:[element.algorithm previousType:element.type]];
[context saveToStore];
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 == MPAlertChangeCounter) {
switch (returnCode) {
case NSAlertDefaultReturn: {
// "New Password" button.
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPElementEntity *element = [self.representedObject entityInContext:context];
if ([element isKindOfClass:[MPElementGeneratedEntity class]]) {
MPElementGeneratedEntity *generatedElement = (MPElementGeneratedEntity *)element;
++generatedElement.counter;
[context saveToStore];
self.representedObject = [[MPElementModel alloc] initWithEntity:element];
}
}];
break;
}
case NSAlertAlternateReturn: {
// "Cancel" button.
break;
}
case NSAlertOtherReturn: {
// "Initial Password" button.
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPElementEntity *element = [self.representedObject entityInContext:context];
if ([element isKindOfClass:[MPElementGeneratedEntity class]]) {
MPElementGeneratedEntity *generatedElement = (MPElementGeneratedEntity *)element;
generatedElement.counter = 1;
[context saveToStore];
self.representedObject = [[MPElementModel alloc] initWithEntity:element];
}
}];
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;
}
}
@end

View File

@ -21,11 +21,14 @@
@interface MPElementModel : NSObject
@property (nonatomic) NSString *site;
@property (nonatomic) NSString *type;
@property (nonatomic) MPElementType type;
@property (nonatomic) NSString *typeName;
@property (nonatomic) NSString *content;
@property (nonatomic) NSString *loginName;
@property (nonatomic) NSNumber *uses;
@property (nonatomic) NSNumber *counter;
@property (nonatomic) NSDate *lastUsed;
@property (nonatomic, strong) id<MPAlgorithm> algorithm;
- (MPElementEntity *)entityForMainThread;
- (MPElementEntity *)entityInContext:(NSManagedObjectContext *)moc;

View File

@ -39,9 +39,12 @@
self.site = entity.name;
self.lastUsed = entity.lastUsed;
self.loginName = entity.loginName;
self.type = entity.typeName;
self.type = entity.type;
self.typeName = entity.typeName;
self.uses = entity.uses_;
self.counter = @([entity isKindOfClass:[MPElementGeneratedEntity class]]? [(MPElementGeneratedEntity *)entity counter]: 0);
self.content = [entity.algorithm resolveContentForElement:entity usingKey:[MPAppDelegate_Shared get].key];
self.algorithm = entity.algorithm;
self.entityOID = entity.objectID;
return self;

View File

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="4514" systemVersion="13B3116" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment defaultVersion="1080" identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="4514"/>
</dependencies>
<objects>
@ -16,7 +15,7 @@
<window allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" wantsToBeColor="NO" animationBehavior="default" id="1">
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
<rect key="contentRect" x="196" y="240" width="833" height="540"/>
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="878"/>
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="900"/>
<view key="contentView" id="2" userLabel="Container">
<rect key="frame" x="0.0" y="0.0" width="833" height="540"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>

View File

@ -7,12 +7,13 @@
//
#import <Cocoa/Cocoa.h>
@class MPElementModel;
@interface MPPasswordWindowController : NSWindowController<NSTextFieldDelegate>
@interface MPPasswordWindowController : NSWindowController<NSTextFieldDelegate, NSCollectionViewDelegate>
@property(nonatomic, strong) NSMutableArray *elements;
@property(nonatomic, strong) NSIndexSet *elementSelectionIndexes;
@property(nonatomic, weak) IBOutlet NSTextField *siteField;
@property(nonatomic, weak) IBOutlet NSTextField *tipField;
@property(nonatomic, weak) IBOutlet NSView *contentContainer;
@property(nonatomic, weak) IBOutlet NSTextField *userLabel;
@property(nonatomic, weak) IBOutlet NSCollectionView *siteCollectionView;

View File

@ -42,8 +42,6 @@
self.backgroundQueue = [NSOperationQueue new];
self.backgroundQueue.maxConcurrentOperationCount = 1;
[self.tipField setStringValue:@""];
[self.userLabel setStringValue:PearlString( @"%@'s password for:", [[MPMacAppDelegate get] activeUserForMainThread].name )];
[[MPMacAppDelegate get] addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
// [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
@ -160,7 +158,6 @@
return;
[self.siteField setStringValue:@""];
[self.tipField setStringValue:@""];
NSAlert *alert = [NSAlert alertWithMessageText:@"Master Password is locked."
defaultButton:@"Unlock" alternateButton:@"Change" otherButton:@"Cancel"
@ -265,12 +262,49 @@
}
}
#pragma mark - NSCollectionViewDelegate
- (void)setElementSelectionIndexes:(NSIndexSet *)elementSelectionIndexes {
// First reset bounds.
PearlMainThread(^{
NSUInteger selectedIndex = self.elementSelectionIndexes.firstIndex;
if (selectedIndex != NSNotFound && selectedIndex < self.elements.count)
[[self selectedView].animator setBoundsOrigin:NSZeroPoint];
} );
_elementSelectionIndexes = elementSelectionIndexes;
}
#pragma mark - NSTextFieldDelegate
- (BOOL)control:(NSControl *)control textView:(NSTextView *)fieldEditor doCommandBySelector:(SEL)commandSelector {
if (commandSelector == @selector(cancel:)) { // Escape without completion.
[self close];
return YES;
}
if (self.elements.count) {
if (commandSelector == @selector(moveUp:)) {
self.elementSelectionIndexes =
[NSIndexSet indexSetWithIndex:(self.elementSelectionIndexes.firstIndex - 1 + self.elements.count) % self.elements.count];
return NO;
}
if (commandSelector == @selector(moveDown:)) {
self.elementSelectionIndexes =
[NSIndexSet indexSetWithIndex:(self.elementSelectionIndexes.firstIndex + 1) % self.elements.count];
return NO;
}
if (commandSelector == @selector(moveLeft:)) {
[[self selectedView].animator setBoundsOrigin:NSZeroPoint];
return NO;
}
if (commandSelector == @selector(moveRight:)) {
NSBox *selectedView = [self selectedView];
[selectedView.animator setBoundsOrigin:NSMakePoint( selectedView.bounds.size.width / 2, 0 )];
return NO;
}
}
// if ((self.siteFieldPreventCompletion = [NSStringFromSelector( commandSelector ) hasPrefix:@"delete"])) { // Backspace any time.
// _activeElementOID = nil;
// [self trySiteWithAction:NO];
@ -278,7 +312,7 @@
// }
if (commandSelector == @selector(insertNewline:)) { // Return without completion.
[self useSite];
return YES;
return NO;
}
return NO;
@ -305,6 +339,8 @@
[self updateElements];
}
#pragma mark - Private
- (void)updateElements {
NSString *query = [self.siteField.currentEditor string];
if (![query length] || ![MPMacAppDelegate get].key) {
@ -327,17 +363,32 @@
for (MPElementEntity *element in siteResults)
[newElements addObject:[[MPElementModel alloc] initWithEntity:element]];
self.elements = newElements;
if (!self.selectedElement && [newElements count])
self.elementSelectionIndexes = [NSIndexSet indexSetWithIndex:0];
}
}];
}
- (MPElementModel *)selectedElement {
- (NSBox *)selectedView {
NSIndexSet *selectedIndexes = self.siteCollectionView.selectionIndexes;
if (!selectedIndexes.count)
NSUInteger selectedIndex = self.elementSelectionIndexes.firstIndex;
if (selectedIndex == NSNotFound || selectedIndex >= self.elements.count)
return nil;
return (MPElementModel *)self.elements[selectedIndexes.firstIndex];
return (NSBox *)[self.siteCollectionView itemAtIndex:selectedIndex].view;
}
- (MPElementModel *)selectedElement {
if (!self.elementSelectionIndexes.count)
return nil;
return (MPElementModel *)self.elements[self.elementSelectionIndexes.firstIndex];
}
- (void)setSelectedElement:(MPElementModel *)element {
self.elementSelectionIndexes = [NSIndexSet indexSetWithIndex:[self.elements indexOfObject:element]];
}
- (void)useSite {
@ -346,60 +397,22 @@
if (selectedElement) {
// Performing action while content is available. Copy it.
[self copyContent:selectedElement.content];
self.tipField.alphaValue = 1;
self.tipField.stringValue = @"Copied! Hit ⎋ (ESC) to close window.";
dispatch_after( dispatch_time( DISPATCH_TIME_NOW, (int64_t)(5.0f * NSEC_PER_SEC) ), dispatch_get_main_queue(),
^{
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:0.2f];
[self.tipField.animator setAlphaValue:0];
[NSAnimationContext endGrouping];
NSBox *selectedView = [self selectedView];
dispatch_async( dispatch_get_main_queue(), ^{
[selectedView.animator setFillColor:[NSColor controlHighlightColor]];
dispatch_after( dispatch_time( DISPATCH_TIME_NOW, (int64_t)(0.3f * NSEC_PER_SEC) ), dispatch_get_main_queue(), ^{
dispatch_async( dispatch_get_main_queue(), ^{
[selectedView.animator setFillColor:[NSColor selectedControlColor]];
} );
} );
} );
}
else {
NSString *siteName = [self.siteField stringValue];
if ([siteName length]) {
if ([siteName length])
// Performing action without content but a site name is written.
[self createNewSite:siteName];
[self.tipField setStringValue:@""];
}
}
// [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
// MPElementEntity *activeElement = [self activeElementInContext:context];
// NSString *typeName = [activeElement typeShortName];
// [activeElement.algorithm resolveContentForElement:activeElement usingKey:[MPAppDelegate_Shared get].key result:
// ^(NSString *result) {
// BOOL actionHandled = NO;
// if (doAction) {
// }
//
// [[NSOperationQueue mainQueue] addOperationWithBlock:^{
// [self setContent:result];
// if (actionHandled)
// [self.tipField setStringValue:@""];
// else if (![result length]) {
// if ([siteName length])
// [self.tipField setStringValue:@"Hit ⌤ (ENTER) to create a new site."];
// else
// [self.tipField setStringValue:@""];
// }
// else if (!doAction)
// [self.tipField setStringValue:@"Hit ⌤ (ENTER) to copy the password."];
// else {
// [self.tipField setStringValue:@"Copied! Hit ⎋ (ESC) to close window."];
// dispatch_after( dispatch_time( DISPATCH_TIME_NOW, (int64_t)(5.0f * NSEC_PER_SEC) ), dispatch_get_main_queue(),
// ^{
// [NSAnimationContext beginGrouping];
// [[NSAnimationContext currentContext] setDuration:0.2f];
// [self.tipField.animator setAlphaValue:0];
// [NSAnimationContext endGrouping];
// } );
// }
// }];
// }];
// }];
}
- (void)copyContent:(NSString *)content {
@ -427,6 +440,8 @@
}];
}
#pragma mark - KVO
- (void)insertObject:(MPElementModel *)model inElementsAtIndex:(NSUInteger)index {
[self.elements insertObject:model atIndex:index];

View File

@ -10,7 +10,6 @@
<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="tipField" destination="183" id="226"/>
<outlet property="userLabel" destination="216" id="223"/>
<outlet property="window" destination="22" id="40"/>
</connections>
@ -19,20 +18,20 @@
<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="373"/>
<rect key="contentRect" x="600" y="530" width="480" height="320"/>
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="878"/>
<value key="minSize" type="size" width="480" height="199"/>
<value key="maxSize" type="size" width="480" height="200"/>
<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="373"/>
<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="373"/>
<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="306" width="200" height="22"/>
<rect key="frame" x="140" y="253" width="200" height="22"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<constraints>
<constraint firstAttribute="width" constant="200" id="258"/>
@ -46,21 +45,8 @@
<outlet property="delegate" destination="-2" id="188"/>
</connections>
</textField>
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="183">
<rect key="frame" x="18" y="20" width="444" height="17"/>
<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.90000000000000002" colorSpace="calibratedWhite"/>
</shadow>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" alignment="center" title="Hit enter to copy the password." usesSingleLineMode="YES" id="184">
<font key="font" metaFont="system"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="216">
<rect key="frame" x="130" y="336" width="221" height="17"/>
<rect key="frame" x="130" y="283" width="221" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<shadow key="shadow" blurRadius="1">
<size key="offset" width="0.0" height="1"/>
@ -72,20 +58,21 @@
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<scrollView autohidesScrollers="YES" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="tjI-mV-s8H">
<rect key="frame" x="0.0" y="45" width="480" height="253"/>
<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="245"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<clipView key="contentView" id="mfh-QT-ClZ">
<rect key="frame" x="1" y="1" width="478" height="251"/>
<rect key="frame" x="1" y="1" width="478" height="243"/>
<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="251"/>
<rect key="frame" x="0.0" y="0.0" width="478" height="243"/>
<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="itemPrototype" destination="QBZ-sO-HQn" id="QAi-HN-YUc"/>
</connections>
</collectionView>
@ -96,18 +83,15 @@
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="vs6-1G-hvM">
<rect key="frame" x="234" y="1" width="15" height="143"/>
<rect key="frame" x="-100" y="-100" width="15" height="143"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
</subviews>
<constraints>
<constraint firstItem="183" firstAttribute="leading" secondItem="143" secondAttribute="leading" constant="20" symbolic="YES" id="200"/>
<constraint firstAttribute="trailing" secondItem="183" secondAttribute="trailing" constant="20" symbolic="YES" id="201"/>
<constraint firstAttribute="bottom" secondItem="183" secondAttribute="bottom" constant="20" symbolic="YES" id="209"/>
<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 firstItem="183" firstAttribute="top" secondItem="tjI-mV-s8H" secondAttribute="bottom" constant="8" symbolic="YES" id="9Vk-0h-GEZ"/>
<constraint firstAttribute="bottom" secondItem="tjI-mV-s8H" secondAttribute="bottom" id="6y8-tJ-1sX"/>
<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"/>
@ -127,98 +111,175 @@
<outlet property="delegate" destination="-2" id="39"/>
</connections>
</window>
<collectionViewItem id="QBZ-sO-HQn">
<collectionViewItem id="QBZ-sO-HQn" customClass="MPElementCollectionView">
<connections>
<outlet property="view" destination="qT3-mQ-aNP" id="0Bp-d3-Fhc"/>
<outlet property="view" destination="bhu-Ky-PQq" id="jZh-jC-6bL"/>
</connections>
</collectionViewItem>
<view wantsLayer="YES" id="qT3-mQ-aNP">
<rect key="frame" x="0.0" y="0.0" width="480" height="63"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="xwJ-Pu-glP">
<rect key="frame" x="6" y="38" width="70" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="left" title="apple.com" id="ymH-M0-M5d">
<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="value" keyPath="representedObject.site" id="qtG-zK-Kqm"/>
</connections>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Cn5-nt-e6X">
<rect key="frame" x="343" y="38" width="131" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="lhunath@lyndir.com" id="Px4-pS-kwG">
<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="value" keyPath="representedObject.loginName" id="IBW-j2-pi0"/>
</connections>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="a1n-Sf-Mw6">
<rect key="frame" x="6" y="8" width="207" height="35"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<constraints>
<constraint firstAttribute="height" constant="35" id="mdi-n9-noL"/>
</constraints>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="left" title="RutuTutnTeni4," id="7jb-Fa-xX3">
<font key="font" size="24" name="Menlo-Regular"/>
<color key="textColor" name="headerColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<binding destination="QBZ-sO-HQn" name="value" keyPath="representedObject.content" id="MRG-PR-8IO"/>
</connections>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="mp4-r1-7Xg">
<rect key="frame" x="267" y="8" width="207" height="35"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<constraints>
<constraint firstAttribute="height" constant="35" id="dU6-nc-x6h"/>
</constraints>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="12" id="Vb9-nO-cWY">
<font key="font" size="24" name="Menlo-Regular"/>
<color key="textColor" name="headerColor" 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="QEe-6y-lip"/>
</connections>
</textField>
</subviews>
<constraints>
<constraint firstItem="a1n-Sf-Mw6" firstAttribute="leading" secondItem="qT3-mQ-aNP" secondAttribute="leading" constant="8" id="08s-TV-lf5"/>
<constraint firstAttribute="trailing" secondItem="Cn5-nt-e6X" secondAttribute="trailing" constant="8" id="21i-fq-NLo"/>
<constraint firstItem="xwJ-Pu-glP" firstAttribute="leading" secondItem="qT3-mQ-aNP" secondAttribute="leading" constant="8" id="Gwd-ln-Nsx"/>
<constraint firstItem="mp4-r1-7Xg" firstAttribute="top" secondItem="Cn5-nt-e6X" secondAttribute="bottom" constant="-5" id="MB5-53-Khp"/>
<constraint firstItem="a1n-Sf-Mw6" firstAttribute="top" secondItem="xwJ-Pu-glP" secondAttribute="bottom" constant="-5" id="hVN-ux-Os9"/>
<constraint firstAttribute="trailing" secondItem="mp4-r1-7Xg" secondAttribute="trailing" constant="8" id="hv0-qD-0tP"/>
<constraint firstItem="Cn5-nt-e6X" firstAttribute="top" secondItem="qT3-mQ-aNP" secondAttribute="top" constant="8" id="lfU-7M-U8M"/>
<constraint firstItem="xwJ-Pu-glP" firstAttribute="top" secondItem="qT3-mQ-aNP" secondAttribute="top" constant="8" id="nbr-g3-Zgg"/>
<constraint firstAttribute="bottom" secondItem="a1n-Sf-Mw6" secondAttribute="bottom" constant="8" id="vfQ-H0-s3U"/>
</constraints>
</view>
<arrayController objectClassName="MPElementModel" id="jTN-Q9-Ajn">
<declaredKeys>
<string>site</string>
<string>type</string>
<string>content</string>
<string>loginName</string>
<string>uses</string>
<string>lastUsed</string>
</declaredKeys>
<connections>
<binding destination="-2" name="contentArray" keyPath="self.elements" id="8Uz-14-YG0"/>
</connections>
</arrayController>
<view id="J0B-yE-vAw">
<rect key="frame" x="0.0" y="0.0" width="100" height="100"/>
<autoresizingMask key="autoresizingMask"/>
</view>
<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="36" width="70" height="17"/>
<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" id="ymH-M0-M5d">
<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="value" keyPath="representedObject.site" id="4as-ow-WbD"/>
</connections>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Cn5-nt-e6X">
<rect key="frame" x="342" y="36" width="131" height="17"/>
<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" id="Px4-pS-kwG">
<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="value" keyPath="representedObject.loginName" id="kuR-ui-n33"/>
</connections>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="a1n-Sf-Mw6">
<rect key="frame" x="6" y="8" width="207" height="35"/>
<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="Menlo-Regular"/>
<color key="textColor" name="headerColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<binding destination="QBZ-sO-HQn" name="value" keyPath="representedObject.content" id="19u-Zx-3vP"/>
</connections>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="mp4-r1-7Xg">
<rect key="frame" x="440" y="8" width="33" height="35"/>
<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="Menlo-Regular"/>
<color key="textColor" name="headerColor" 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="7gd-qw-W5L"/>
</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="oC2-zu-Voi">
<rect key="frame" x="480" y="-1" width="160" height="63"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<constraints>
<constraint firstAttribute="height" constant="61" id="228-Aw-hiL"/>
</constraints>
<buttonCell key="cell" type="smallSquare" bezelStyle="smallSquare" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="5ZG-4S-zov">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<string key="title">Type:
Long Password</string>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="toggleType:" target="QBZ-sO-HQn" id="pdJ-Uz-fac"/>
<binding destination="QBZ-sO-HQn" name="title" keyPath="typeTitle" id="Ric-aa-82E"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="sEM-ko-UxJ">
<rect key="frame" x="640" y="-1" width="160" height="63"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="smallSquare" bezelStyle="smallSquare" imagePosition="overlaps" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="ymB-jq-V5d">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<string key="title">Login Name:
lhunath@lyndir.com</string>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="setLoginName:" target="QBZ-sO-HQn" id="xMV-pi-ofl"/>
<binding destination="QBZ-sO-HQn" name="title" keyPath="loginNameTitle" id="4Bw-5R-OCr"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="SA7-eS-Kal" userLabel="Square - Number: 1">
<rect key="frame" x="800" y="-1" width="160" height="63"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="smallSquare" bezelStyle="smallSquare" imagePosition="overlaps" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="7b3-Hz-grn">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<string key="title">Number:
1</string>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="incrementCounter:" target="QBZ-sO-HQn" id="dZP-Rc-LHA"/>
<binding destination="QBZ-sO-HQn" name="title" keyPath="counterTitle" id="cGQ-ec-Yyy"/>
</connections>
</button>
</subviews>
</view>
<constraints>
<constraint firstAttribute="centerY" secondItem="SA7-eS-Kal" secondAttribute="centerY" id="72p-MU-EeL"/>
<constraint firstItem="SA7-eS-Kal" firstAttribute="height" secondItem="oC2-zu-Voi" secondAttribute="height" id="8ev-ss-Omh"/>
<constraint firstAttribute="trailing" secondItem="SA7-eS-Kal" secondAttribute="trailing" id="BDj-TF-YFN"/>
<constraint firstAttribute="centerY" secondItem="sEM-ko-UxJ" secondAttribute="centerY" id="F2A-cH-miL"/>
<constraint firstItem="Cn5-nt-e6X" firstAttribute="top" secondItem="bhu-Ky-PQq" secondAttribute="top" constant="8" id="FWS-tY-KkM"/>
<constraint firstItem="SA7-eS-Kal" firstAttribute="width" secondItem="sEM-ko-UxJ" secondAttribute="width" id="HKZ-ha-UXa"/>
<constraint firstItem="tsT-Xh-Nxb" firstAttribute="leading" secondItem="Cn5-nt-e6X" secondAttribute="trailing" constant="8" id="LjD-10-tVU"/>
<constraint firstItem="SA7-eS-Kal" firstAttribute="leading" secondItem="sEM-ko-UxJ" secondAttribute="trailing" id="Maf-za-cvg"/>
<constraint firstItem="oC2-zu-Voi" firstAttribute="leading" secondItem="tsT-Xh-Nxb" secondAttribute="trailing" id="NJd-Us-uj8"/>
<constraint firstItem="tsT-Xh-Nxb" firstAttribute="top" secondItem="bhu-Ky-PQq" secondAttribute="top" id="ODH-Nx-QdY"/>
<constraint firstAttribute="centerY" secondItem="oC2-zu-Voi" secondAttribute="centerY" id="ODK-IE-HEh"/>
<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 firstItem="SA7-eS-Kal" firstAttribute="width" secondItem="oC2-zu-Voi" secondAttribute="width" id="akL-8k-gbu"/>
<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 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="SA7-eS-Kal" firstAttribute="height" secondItem="sEM-ko-UxJ" secondAttribute="height" id="mpf-ND-lSa"/>
<constraint firstItem="sEM-ko-UxJ" firstAttribute="leading" secondItem="oC2-zu-Voi" secondAttribute="trailing" id="tmV-ZM-J1I"/>
<constraint firstItem="xwJ-Pu-glP" firstAttribute="top" secondItem="bhu-Ky-PQq" secondAttribute="top" constant="8" id="z8k-ot-NjU"/>
</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

@ -13,7 +13,6 @@
93D39C5789EFA607CF788082 /* MPElementModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39E73BF5CBF8E5B005CD3 /* MPElementModel.m */; };
93D39C7C2BE7C0E0763B0177 /* MPElementCollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D394495528B10D1B61A2C3 /* MPElementCollectionView.m */; };
93D39E281E3658B30550CB55 /* NSDictionary+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */; };
DA0933CA1747A56A00DE1CEF /* MPInitialWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA0933C91747A56A00DE1CEF /* MPInitialWindow.xib */; };
DA0933CC1747AD2D00DE1CEF /* shot-laptop-leaning-iphone.png in Resources */ = {isa = PBXBuildFile; fileRef = DA0933CB1747AD2D00DE1CEF /* shot-laptop-leaning-iphone.png */; };
DA0933D01747B91B00DE1CEF /* appstore.png in Resources */ = {isa = PBXBuildFile; fileRef = DA0933CF1747B91B00DE1CEF /* appstore.png */; };
DA16B33F170661D4000A0EAB /* libUbiquityStoreManager.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DA4425CB1557BED40052177D /* libUbiquityStoreManager.a */; };
@ -204,6 +203,7 @@
DAEB942718AB0FFD000490CC /* sha256.h in Headers */ = {isa = PBXBuildFile; fileRef = DAEB93D618AB0FFD000490CC /* sha256.h */; };
DAEB942818AB0FFD000490CC /* sysendian.h in Headers */ = {isa = PBXBuildFile; fileRef = DAEB93D718AB0FFD000490CC /* sysendian.h */; };
DAEB942918AB0FFD000490CC /* warn.h in Headers */ = {isa = PBXBuildFile; fileRef = DAEB93D818AB0FFD000490CC /* warn.h */; };
DAEB942E18B47FB3000490CC /* MPInitialWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA0933C91747A56A00DE1CEF /* MPInitialWindow.xib */; };
DAFE4A1315039824003ABA7C /* NSObject+PearlExport.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE45D815039823003ABA7C /* NSObject+PearlExport.h */; };
DAFE4A1415039824003ABA7C /* NSObject+PearlExport.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFE45D915039823003ABA7C /* NSObject+PearlExport.m */; };
DAFE4A1515039824003ABA7C /* NSString+PearlNSArrayFormat.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE45DA15039823003ABA7C /* NSString+PearlNSArrayFormat.h */; };
@ -1452,6 +1452,7 @@
DACA27231705DF81002C6C22 /* avatar-15@2x.png in Resources */,
DACA27241705DF81002C6C22 /* avatar-5@2x.png in Resources */,
DACA27251705DF81002C6C22 /* avatar-6.png in Resources */,
DAEB942E18B47FB3000490CC /* MPInitialWindow.xib in Resources */,
DACA27261705DF81002C6C22 /* avatar-6@2x.png in Resources */,
DACA27271705DF81002C6C22 /* avatar-16@2x.png in Resources */,
DACA27281705DF81002C6C22 /* avatar-10.png in Resources */,
@ -1486,7 +1487,6 @@
DA5E5D0A1724A667003798D8 /* InfoPlist.strings in Resources */,
DA5E5D0B1724A667003798D8 /* MainMenu.xib in Resources */,
DA5E5D551724F9C8003798D8 /* MasterPassword.iconset in Resources */,
DA0933CA1747A56A00DE1CEF /* MPInitialWindow.xib in Resources */,
DA0933CC1747AD2D00DE1CEF /* shot-laptop-leaning-iphone.png in Resources */,
DA0933D01747B91B00DE1CEF /* appstore.png in Resources */,
);