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:
parent
c48bed5ebd
commit
775a6fd4ea
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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: @""];
|
||||
}
|
||||
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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"/>
|
||||
|
@ -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;
|
||||
|
@ -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];
|
||||
|
@ -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"/>
|
||||
<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="38" width="70" height="17"/>
|
||||
<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="qtG-zK-Kqm"/>
|
||||
<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="343" y="38" width="131" height="17"/>
|
||||
<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="IBW-j2-pi0"/>
|
||||
<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"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="35" id="mdi-n9-noL"/>
|
||||
</constraints>
|
||||
<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="MRG-PR-8IO"/>
|
||||
<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="267" y="8" width="207" height="35"/>
|
||||
<rect key="frame" x="440" y="8" width="33" height="35"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="35" id="dU6-nc-x6h"/>
|
||||
</constraints>
|
||||
<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="QEe-6y-lip"/>
|
||||
<binding destination="QBZ-sO-HQn" name="value" keyPath="representedObject.uses" id="7gd-qw-W5L"/>
|
||||
</connections>
|
||||
</textField>
|
||||
</subviews>
|
||||
<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 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"/>
|
||||
<constraint firstAttribute="height" constant="61" id="228-Aw-hiL"/>
|
||||
</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>
|
||||
<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>
|
||||
<binding destination="-2" name="contentArray" keyPath="self.elements" id="8Uz-14-YG0"/>
|
||||
<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>
|
||||
</arrayController>
|
||||
<view id="J0B-yE-vAw">
|
||||
<rect key="frame" x="0.0" y="0.0" width="100" height="100"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</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>
|
@ -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 */,
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user