Mac fixes and improvements to usability.
[FIXED] Mac: Locking shouldn't unset the active user. [IMPROVED] Mac: Behavior of auto-completing site-name field improved. [ADDED] Mac: Alert when MP is incorrect when unlocking.
This commit is contained in:
parent
b07298e203
commit
d5bffd86d6
@ -40,6 +40,8 @@ typedef enum {
|
|||||||
MPElementTypeStoredDevicePrivate = 0x1 | MPElementTypeClassStored | MPElementFeatureDevicePrivate,
|
MPElementTypeStoredDevicePrivate = 0x1 | MPElementTypeClassStored | MPElementFeatureDevicePrivate,
|
||||||
} MPElementType;
|
} MPElementType;
|
||||||
|
|
||||||
|
#define MPErrorDomain @"MPErrorDomain"
|
||||||
|
|
||||||
#define MPCheckpointHelpChapter @"MPCheckpointHelpChapter"
|
#define MPCheckpointHelpChapter @"MPCheckpointHelpChapter"
|
||||||
#define MPCheckpointCopyToPasteboard @"MPCheckpointCopyToPasteboard"
|
#define MPCheckpointCopyToPasteboard @"MPCheckpointCopyToPasteboard"
|
||||||
#define MPCheckpointCopyLoginNameToPasteboard @"MPCheckpointCopyLoginNameToPasteboard"
|
#define MPCheckpointCopyLoginNameToPasteboard @"MPCheckpointCopyLoginNameToPasteboard"
|
||||||
|
@ -29,5 +29,6 @@
|
|||||||
- (IBAction)togglePreference:(NSMenuItem *)sender;
|
- (IBAction)togglePreference:(NSMenuItem *)sender;
|
||||||
- (IBAction)newUser:(NSMenuItem *)sender;
|
- (IBAction)newUser:(NSMenuItem *)sender;
|
||||||
- (IBAction)signOut:(id)sender;
|
- (IBAction)signOut:(id)sender;
|
||||||
|
- (IBAction)lock:(id)sender;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -37,7 +37,7 @@ static EventHotKeyID MPLockHotKey = {.signature = 'lock', .id = 1};
|
|||||||
[MPMacConfig get];
|
[MPMacConfig get];
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
[PearlLogger get].printLevel = PearlLogLevelTrace;
|
[PearlLogger get].printLevel = PearlLogLevelDebug;//Trace;
|
||||||
#endif
|
#endif
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -60,7 +60,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
|||||||
return noErr;
|
return noErr;
|
||||||
}
|
}
|
||||||
if (hotKeyID.signature == MPLockHotKey.signature && hotKeyID.id == MPLockHotKey.id) {
|
if (hotKeyID.signature == MPLockHotKey.signature && hotKeyID.id == MPLockHotKey.id) {
|
||||||
[((__bridge MPAppDelegate *)userData) signOut:nil];
|
[((__bridge MPAppDelegate *)userData) lock:nil];
|
||||||
return noErr;
|
return noErr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,6 +163,11 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
|||||||
[self signOutAnimated:YES];
|
[self signOutAnimated:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (IBAction)lock:(id)sender {
|
||||||
|
|
||||||
|
self.key = nil;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)didUpdateConfigForKey:(SEL)configKey fromValue:(id)oldValue {
|
- (void)didUpdateConfigForKey:(SEL)configKey fromValue:(id)oldValue {
|
||||||
|
|
||||||
if (configKey == @selector(rememberLogin))
|
if (configKey == @selector(rememberLogin))
|
||||||
|
@ -11,22 +11,19 @@
|
|||||||
#import "MPAppDelegate_Key.h"
|
#import "MPAppDelegate_Key.h"
|
||||||
#import "MPAppDelegate_Store.h"
|
#import "MPAppDelegate_Store.h"
|
||||||
|
|
||||||
|
#define MPAlertUnlockMP @"MPAlertUnlockMP"
|
||||||
|
#define MPAlertIncorrectMP @"MPAlertIncorrectMP"
|
||||||
|
|
||||||
|
|
||||||
@interface MPPasswordWindowController ()
|
@interface MPPasswordWindowController ()
|
||||||
|
|
||||||
@property (nonatomic, strong) NSString *oldSiteName;
|
|
||||||
@property (nonatomic, strong) NSArray /* MPElementEntity */ *siteResults;
|
@property (nonatomic, strong) NSArray /* MPElementEntity */ *siteResults;
|
||||||
@property (nonatomic) BOOL inProgress;
|
@property (nonatomic) BOOL inProgress;
|
||||||
|
@property (nonatomic) BOOL siteFieldPreventCompletion;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation MPPasswordWindowController
|
@implementation MPPasswordWindowController
|
||||||
@synthesize oldSiteName, siteResults;
|
|
||||||
@synthesize siteField;
|
|
||||||
@synthesize contentField;
|
|
||||||
@synthesize tipField;
|
|
||||||
@synthesize inProgress = _inProgress;
|
|
||||||
|
|
||||||
|
|
||||||
- (void)windowDidLoad {
|
- (void)windowDidLoad {
|
||||||
|
|
||||||
@ -37,6 +34,8 @@
|
|||||||
[self.userLabel setStringValue:PearlString(@"%@'s password for:", [MPAppDelegate get].activeUser.name)];
|
[self.userLabel setStringValue:PearlString(@"%@'s password for:", [MPAppDelegate get].activeUser.name)];
|
||||||
} forKeyPath:@"activeUser" options:NSKeyValueObservingOptionInitial context:nil];
|
} forKeyPath:@"activeUser" options:NSKeyValueObservingOptionInitial context:nil];
|
||||||
[[MPAppDelegate get] addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
|
[[MPAppDelegate get] addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
|
||||||
|
if (![MPAppDelegate get].key)
|
||||||
|
[self unlock];
|
||||||
if ([MPAppDelegate get].activeUser && [MPAppDelegate get].key)
|
if ([MPAppDelegate get].activeUser && [MPAppDelegate get].key)
|
||||||
[MPAlgorithmDefault migrateUser:[MPAppDelegate get].activeUser completion:^(BOOL userRequiresNewMigration) {
|
[MPAlgorithmDefault migrateUser:[MPAppDelegate get].activeUser completion:^(BOOL userRequiresNewMigration) {
|
||||||
if (userRequiresNewMigration)
|
if (userRequiresNewMigration)
|
||||||
@ -56,18 +55,6 @@
|
|||||||
usingBlock:^(NSNotification *note) {
|
usingBlock:^(NSNotification *note) {
|
||||||
[[NSApplication sharedApplication] hide:self];
|
[[NSApplication sharedApplication] hide:self];
|
||||||
}];
|
}];
|
||||||
[[NSNotificationCenter defaultCenter] addObserverForName:NSControlTextDidChangeNotification object:self.siteField queue:nil
|
|
||||||
usingBlock:^(NSNotification *note) {
|
|
||||||
NSString *newSiteName = [self.siteField stringValue];
|
|
||||||
BOOL shouldComplete = [self.oldSiteName length] < [newSiteName length];
|
|
||||||
self.oldSiteName = newSiteName;
|
|
||||||
|
|
||||||
if ([self trySite])
|
|
||||||
shouldComplete = NO;
|
|
||||||
|
|
||||||
if (shouldComplete)
|
|
||||||
[[[note userInfo] objectForKey:@"NSFieldEditor"] complete:nil];
|
|
||||||
}];
|
|
||||||
[[NSNotificationCenter defaultCenter] addObserverForName:MPNotificationSignedOut object:nil queue:nil
|
[[NSNotificationCenter defaultCenter] addObserverForName:MPNotificationSignedOut object:nil queue:nil
|
||||||
usingBlock:^(NSNotification *note) {
|
usingBlock:^(NSNotification *note) {
|
||||||
[self.window close];
|
[self.window close];
|
||||||
@ -78,6 +65,12 @@
|
|||||||
|
|
||||||
- (void)unlock {
|
- (void)unlock {
|
||||||
|
|
||||||
|
if (![MPAppDelegate get].activeUser)
|
||||||
|
// No user to sign in with.
|
||||||
|
return;
|
||||||
|
if ([MPAppDelegate get].key)
|
||||||
|
// Already logged in.
|
||||||
|
return;
|
||||||
if ([[MPAppDelegate get] signInAsUser:[MPAppDelegate get].activeUser usingMasterPassword:nil])
|
if ([[MPAppDelegate get] signInAsUser:[MPAppDelegate get].activeUser usingMasterPassword:nil])
|
||||||
// Load the key from the keychain.
|
// Load the key from the keychain.
|
||||||
return;
|
return;
|
||||||
@ -88,6 +81,10 @@
|
|||||||
if ([MPAppDelegate get].key)
|
if ([MPAppDelegate get].key)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
self.content = @"";
|
||||||
|
[self.siteField setStringValue:@""];
|
||||||
|
[self.tipField setStringValue:@""];
|
||||||
|
|
||||||
NSAlert *alert = [NSAlert alertWithMessageText:@"Master Password is locked."
|
NSAlert *alert = [NSAlert alertWithMessageText:@"Master Password is locked."
|
||||||
defaultButton:@"Unlock" alternateButton:@"Change" otherButton:@"Cancel"
|
defaultButton:@"Unlock" alternateButton:@"Change" otherButton:@"Cancel"
|
||||||
informativeTextWithFormat:@"The master password is required to unlock the application for:\n\n%@", [MPAppDelegate get].activeUser.name];
|
informativeTextWithFormat:@"The master password is required to unlock the application for:\n\n%@", [MPAppDelegate get].activeUser.name];
|
||||||
@ -96,57 +93,71 @@
|
|||||||
[alert layout];
|
[alert layout];
|
||||||
[passwordField becomeFirstResponder];
|
[passwordField becomeFirstResponder];
|
||||||
[alert beginSheetModalForWindow:self.window modalDelegate:self
|
[alert beginSheetModalForWindow:self.window modalDelegate:self
|
||||||
didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:NULL];
|
didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:MPAlertUnlockMP];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo {
|
- (void)alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo {
|
||||||
|
|
||||||
switch (returnCode) {
|
if (contextInfo == MPAlertIncorrectMP) {
|
||||||
case NSAlertAlternateReturn:
|
[self.window close];
|
||||||
// "Change" button.
|
return;
|
||||||
if ([[NSAlert alertWithMessageText:@"Changing Master Password"
|
}
|
||||||
defaultButton:nil alternateButton:[PearlStrings get].commonButtonCancel otherButton:nil
|
if (contextInfo == MPAlertUnlockMP) {
|
||||||
informativeTextWithFormat:
|
switch (returnCode) {
|
||||||
@"This will allow you to log in with a different master password.\n\n"
|
case NSAlertAlternateReturn:
|
||||||
@"Note that you will only see the sites and passwords for the master password you log in with.\n"
|
// "Change" button.
|
||||||
@"If you log in with a different master password, your current sites will be unavailable.\n\n"
|
if ([[NSAlert alertWithMessageText:@"Changing Master Password"
|
||||||
@"You can always change back to your current master password later.\n"
|
defaultButton:nil alternateButton:[PearlStrings get].commonButtonCancel otherButton:nil
|
||||||
@"Your current sites and passwords will then become available again."] runModal]
|
informativeTextWithFormat:
|
||||||
== 1) {
|
@"This will allow you to log in with a different master password.\n\n"
|
||||||
[MPAppDelegate get].activeUser.keyID = nil;
|
@"Note that you will only see the sites and passwords for the master password you log in with.\n"
|
||||||
[[MPAppDelegate get] forgetSavedKeyFor:[MPAppDelegate get].activeUser];
|
@"If you log in with a different master password, your current sites will be unavailable.\n\n"
|
||||||
[[MPAppDelegate get] signOutAnimated:YES];
|
@"You can always change back to your current master password later.\n"
|
||||||
}
|
@"Your current sites and passwords will then become available again."] runModal]
|
||||||
break;
|
== 1) {
|
||||||
|
[MPAppDelegate get].activeUser.keyID = nil;
|
||||||
|
[[MPAppDelegate get] forgetSavedKeyFor:[MPAppDelegate get].activeUser];
|
||||||
|
[[MPAppDelegate get] signOutAnimated:YES];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case NSAlertOtherReturn:
|
case NSAlertOtherReturn:
|
||||||
// "Cancel" button.
|
// "Cancel" button.
|
||||||
[self.window close];
|
[self.window close];
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case NSAlertDefaultReturn: {
|
case NSAlertDefaultReturn: {
|
||||||
// "Unlock" button.
|
// "Unlock" button.
|
||||||
self.contentContainer.alphaValue = 0;
|
self.contentContainer.alphaValue = 0;
|
||||||
[self.progressView startAnimation:nil];
|
[self.progressView startAnimation:nil];
|
||||||
self.inProgress = YES;
|
self.inProgress = YES;
|
||||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
|
||||||
BOOL success = [[MPAppDelegate get] signInAsUser:[MPAppDelegate get].activeUser usingMasterPassword:[(NSSecureTextField *)alert.accessoryView stringValue]];
|
BOOL success = [[MPAppDelegate get] signInAsUser:[MPAppDelegate get].activeUser
|
||||||
self.inProgress = NO;
|
usingMasterPassword:[(NSSecureTextField *)alert.accessoryView stringValue]];
|
||||||
|
self.inProgress = NO;
|
||||||
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
[self.progressView stopAnimation:nil];
|
[self.progressView stopAnimation:nil];
|
||||||
|
|
||||||
if (success)
|
if (success)
|
||||||
self.contentContainer.alphaValue = 1;
|
self.contentContainer.alphaValue = 1;
|
||||||
else
|
else {
|
||||||
[self.window close];
|
[[NSAlert alertWithError:[NSError errorWithDomain:MPErrorDomain code:0 userInfo:@{
|
||||||
|
NSLocalizedDescriptionKey : PearlString(@"Incorrect master password for user %@",
|
||||||
|
[MPAppDelegate get].activeUser.name)
|
||||||
|
}]] beginSheetModalForWindow:self.window modalDelegate:self
|
||||||
|
didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:MPAlertIncorrectMP];
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
return;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,19 +170,27 @@
|
|||||||
|
|
||||||
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([MPElementEntity class])];
|
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([MPElementEntity class])];
|
||||||
fetchRequest.sortDescriptors = [NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:@"uses_" ascending:NO]];
|
fetchRequest.sortDescriptors = [NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:@"uses_" ascending:NO]];
|
||||||
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"(%@ == '' OR name BEGINSWITH[cd] %@) AND user == %@",
|
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"(name BEGINSWITH[cd] %@) AND user == %@",
|
||||||
query, query, [MPAppDelegate get].activeUser];
|
query, [MPAppDelegate get].activeUser];
|
||||||
|
|
||||||
NSError *error = nil;
|
NSError *error = nil;
|
||||||
self.siteResults = [[MPAppDelegate managedObjectContextIfReady] executeFetchRequest:fetchRequest error:&error];
|
self.siteResults = [[MPAppDelegate managedObjectContextIfReady] executeFetchRequest:fetchRequest error:&error];
|
||||||
if (error)
|
if (error)
|
||||||
err(@"Couldn't fetch elements: %@", error);
|
err(@"While fetching elements for completion: %@", error);
|
||||||
|
|
||||||
|
if ([self.siteResults count] == 1) {
|
||||||
|
[textView setString:[(MPElementEntity *)[self.siteResults objectAtIndex:0] name]];
|
||||||
|
[textView setSelectedRange:NSMakeRange([query length], [[textView string] length] - [query length])];
|
||||||
|
if ([self trySite])
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
NSMutableArray *mutableResults = [NSMutableArray arrayWithCapacity:[self.siteResults count] + 1];
|
NSMutableArray *mutableResults = [NSMutableArray arrayWithCapacity:[self.siteResults count] + 1];
|
||||||
if (self.siteResults)
|
if (self.siteResults)
|
||||||
for (MPElementEntity *element in self.siteResults)
|
for (MPElementEntity *element in self.siteResults)
|
||||||
[mutableResults addObject:element.name];
|
[mutableResults addObject:element.name];
|
||||||
// [mutableResults addObject:query]; // For when the app should be able to create new sites.
|
// [mutableResults addObject:query]; // For when the app should be able to create new sites.
|
||||||
|
|
||||||
return mutableResults;
|
return mutableResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,32 +200,70 @@
|
|||||||
[self.window close];
|
[self.window close];
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
if ((self.siteFieldPreventCompletion = [NSStringFromSelector(commandSelector) hasPrefix:@"delete"]))
|
||||||
|
return NO;
|
||||||
if (commandSelector == @selector(insertNewline:) && [self.content length]) {
|
if (commandSelector == @selector(insertNewline:) && [self.content length]) {
|
||||||
[[NSPasteboard generalPasteboard] declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
|
if ([self trySite])
|
||||||
if ([[NSPasteboard generalPasteboard] setString:self.content forType:NSPasteboardTypeString]) {
|
[self copyContents];
|
||||||
self.tipField.alphaValue = 1;
|
return YES;
|
||||||
[self.tipField setStringValue:@"Copied! Hit ⎋ (ESC) to close window."];
|
|
||||||
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0f * NSEC_PER_SEC));
|
|
||||||
dispatch_after(popTime, dispatch_get_main_queue(), ^{
|
|
||||||
[NSAnimationContext beginGrouping];
|
|
||||||
[[NSAnimationContext currentContext] setDuration:0.2f];
|
|
||||||
[self.tipField.animator setAlphaValue:0];
|
|
||||||
[NSAnimationContext endGrouping];
|
|
||||||
});
|
|
||||||
|
|
||||||
[[self findElement] use];
|
|
||||||
return YES;
|
|
||||||
} else
|
|
||||||
wrn(@"Couldn't copy password to pasteboard.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)controlTextDidEndEditing:(NSNotification *)obj {
|
- (void)copyContents {
|
||||||
|
|
||||||
if (obj.object == self.siteField)
|
[[NSPasteboard generalPasteboard] declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
|
||||||
[self trySite];
|
if (![[NSPasteboard generalPasteboard] setString:self.content forType:NSPasteboardTypeString]) {
|
||||||
|
wrn(@"Couldn't copy password to pasteboard.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
self.tipField.alphaValue = 1;
|
||||||
|
[self.tipField setStringValue:@"Copied! Hit ⎋ (ESC) to close window."];
|
||||||
|
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0f * NSEC_PER_SEC));
|
||||||
|
dispatch_after(popTime, dispatch_get_main_queue(), ^{
|
||||||
|
[NSAnimationContext beginGrouping];
|
||||||
|
[[NSAnimationContext currentContext] setDuration:0.2f];
|
||||||
|
[self.tipField.animator setAlphaValue:0];
|
||||||
|
[NSAnimationContext endGrouping];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
[[self findElement] use];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)controlTextDidEndEditing:(NSNotification *)note {
|
||||||
|
|
||||||
|
if (note.object != self.siteField)
|
||||||
|
return;
|
||||||
|
|
||||||
|
[self trySite];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)controlTextDidChange:(NSNotification *)note {
|
||||||
|
|
||||||
|
if (note.object != self.siteField)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Update the site content as the site name changes.
|
||||||
|
BOOL hasValidSite = [self trySite];
|
||||||
|
|
||||||
|
if ([[NSApp currentEvent] type] == NSKeyDown && [[[NSApp currentEvent] charactersIgnoringModifiers] isEqualToString:@"\r"]) {
|
||||||
|
if (hasValidSite)
|
||||||
|
[self copyContents];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.siteFieldPreventCompletion) {
|
||||||
|
self.siteFieldPreventCompletion = NO;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.siteFieldPreventCompletion = YES;
|
||||||
|
[(NSText *)[note.userInfo objectForKey:@"NSFieldEditor"] complete:self];
|
||||||
|
self.siteFieldPreventCompletion = NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)content {
|
- (NSString *)content {
|
||||||
@ -237,8 +294,6 @@
|
|||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
dbg(@"element:\n%@", [result debugDescription]);
|
|
||||||
|
|
||||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
|
||||||
NSString *description = [result.content description];
|
NSString *description = [result.content description];
|
||||||
if (!description)
|
if (!description)
|
||||||
|
@ -3,12 +3,12 @@
|
|||||||
<data>
|
<data>
|
||||||
<int key="IBDocument.SystemTarget">1070</int>
|
<int key="IBDocument.SystemTarget">1070</int>
|
||||||
<string key="IBDocument.SystemVersion">12C60</string>
|
<string key="IBDocument.SystemVersion">12C60</string>
|
||||||
<string key="IBDocument.InterfaceBuilderVersion">2840</string>
|
<string key="IBDocument.InterfaceBuilderVersion">2844</string>
|
||||||
<string key="IBDocument.AppKitVersion">1187.34</string>
|
<string key="IBDocument.AppKitVersion">1187.34</string>
|
||||||
<string key="IBDocument.HIToolboxVersion">625.00</string>
|
<string key="IBDocument.HIToolboxVersion">625.00</string>
|
||||||
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
|
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
|
||||||
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
|
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||||
<string key="NS.object.0">2840</string>
|
<string key="NS.object.0">2844</string>
|
||||||
</object>
|
</object>
|
||||||
<array key="IBDocument.IntegratedClassDependencies">
|
<array key="IBDocument.IntegratedClassDependencies">
|
||||||
<string>NSCustomObject</string>
|
<string>NSCustomObject</string>
|
||||||
@ -241,14 +241,6 @@
|
|||||||
</object>
|
</object>
|
||||||
<int key="connectionID">726</int>
|
<int key="connectionID">726</int>
|
||||||
</object>
|
</object>
|
||||||
<object class="IBConnectionRecord">
|
|
||||||
<object class="IBActionConnection" key="connection">
|
|
||||||
<string key="label">signOut:</string>
|
|
||||||
<reference key="source" ref="976324537"/>
|
|
||||||
<reference key="destination" ref="229948989"/>
|
|
||||||
</object>
|
|
||||||
<int key="connectionID">727</int>
|
|
||||||
</object>
|
|
||||||
<object class="IBConnectionRecord">
|
<object class="IBConnectionRecord">
|
||||||
<object class="IBOutletConnection" key="connection">
|
<object class="IBOutletConnection" key="connection">
|
||||||
<string key="label">showItem</string>
|
<string key="label">showItem</string>
|
||||||
@ -345,6 +337,14 @@
|
|||||||
</object>
|
</object>
|
||||||
<int key="connectionID">763</int>
|
<int key="connectionID">763</int>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="IBConnectionRecord">
|
||||||
|
<object class="IBActionConnection" key="connection">
|
||||||
|
<string key="label">lock:</string>
|
||||||
|
<reference key="source" ref="976324537"/>
|
||||||
|
<reference key="destination" ref="229948989"/>
|
||||||
|
</object>
|
||||||
|
<int key="connectionID">764</int>
|
||||||
|
</object>
|
||||||
</array>
|
</array>
|
||||||
<object class="IBMutableOrderedSet" key="objectRecords">
|
<object class="IBMutableOrderedSet" key="objectRecords">
|
||||||
<array key="orderedObjects">
|
<array key="orderedObjects">
|
||||||
@ -538,96 +538,9 @@
|
|||||||
<nil key="activeLocalization"/>
|
<nil key="activeLocalization"/>
|
||||||
<dictionary class="NSMutableDictionary" key="localizations"/>
|
<dictionary class="NSMutableDictionary" key="localizations"/>
|
||||||
<nil key="sourceID"/>
|
<nil key="sourceID"/>
|
||||||
<int key="maxID">763</int>
|
<int key="maxID">764</int>
|
||||||
</object>
|
|
||||||
<object class="IBClassDescriber" key="IBDocument.Classes">
|
|
||||||
<array class="NSMutableArray" key="referencedPartialClassDescriptions">
|
|
||||||
<object class="IBPartialClassDescription">
|
|
||||||
<string key="className">MPAppDelegate</string>
|
|
||||||
<string key="superclassName">MPAppDelegate_Shared</string>
|
|
||||||
<dictionary class="NSMutableDictionary" key="actions">
|
|
||||||
<string key="activate:">id</string>
|
|
||||||
<string key="newUser:">NSMenuItem</string>
|
|
||||||
<string key="signOut:">id</string>
|
|
||||||
<string key="togglePreference:">NSMenuItem</string>
|
|
||||||
</dictionary>
|
|
||||||
<dictionary class="NSMutableDictionary" key="actionInfosByName">
|
|
||||||
<object class="IBActionInfo" key="activate:">
|
|
||||||
<string key="name">activate:</string>
|
|
||||||
<string key="candidateClassName">id</string>
|
|
||||||
</object>
|
|
||||||
<object class="IBActionInfo" key="newUser:">
|
|
||||||
<string key="name">newUser:</string>
|
|
||||||
<string key="candidateClassName">NSMenuItem</string>
|
|
||||||
</object>
|
|
||||||
<object class="IBActionInfo" key="signOut:">
|
|
||||||
<string key="name">signOut:</string>
|
|
||||||
<string key="candidateClassName">id</string>
|
|
||||||
</object>
|
|
||||||
<object class="IBActionInfo" key="togglePreference:">
|
|
||||||
<string key="name">togglePreference:</string>
|
|
||||||
<string key="candidateClassName">NSMenuItem</string>
|
|
||||||
</object>
|
|
||||||
</dictionary>
|
|
||||||
<dictionary class="NSMutableDictionary" key="outlets">
|
|
||||||
<string key="createUserItem">NSMenuItem</string>
|
|
||||||
<string key="lockItem">NSMenuItem</string>
|
|
||||||
<string key="rememberPasswordItem">NSMenuItem</string>
|
|
||||||
<string key="savePasswordItem">NSMenuItem</string>
|
|
||||||
<string key="showItem">NSMenuItem</string>
|
|
||||||
<string key="statusMenu">NSMenu</string>
|
|
||||||
<string key="useICloudItem">NSMenuItem</string>
|
|
||||||
<string key="usersItem">NSMenuItem</string>
|
|
||||||
</dictionary>
|
|
||||||
<dictionary class="NSMutableDictionary" key="toOneOutletInfosByName">
|
|
||||||
<object class="IBToOneOutletInfo" key="createUserItem">
|
|
||||||
<string key="name">createUserItem</string>
|
|
||||||
<string key="candidateClassName">NSMenuItem</string>
|
|
||||||
</object>
|
|
||||||
<object class="IBToOneOutletInfo" key="lockItem">
|
|
||||||
<string key="name">lockItem</string>
|
|
||||||
<string key="candidateClassName">NSMenuItem</string>
|
|
||||||
</object>
|
|
||||||
<object class="IBToOneOutletInfo" key="rememberPasswordItem">
|
|
||||||
<string key="name">rememberPasswordItem</string>
|
|
||||||
<string key="candidateClassName">NSMenuItem</string>
|
|
||||||
</object>
|
|
||||||
<object class="IBToOneOutletInfo" key="savePasswordItem">
|
|
||||||
<string key="name">savePasswordItem</string>
|
|
||||||
<string key="candidateClassName">NSMenuItem</string>
|
|
||||||
</object>
|
|
||||||
<object class="IBToOneOutletInfo" key="showItem">
|
|
||||||
<string key="name">showItem</string>
|
|
||||||
<string key="candidateClassName">NSMenuItem</string>
|
|
||||||
</object>
|
|
||||||
<object class="IBToOneOutletInfo" key="statusMenu">
|
|
||||||
<string key="name">statusMenu</string>
|
|
||||||
<string key="candidateClassName">NSMenu</string>
|
|
||||||
</object>
|
|
||||||
<object class="IBToOneOutletInfo" key="useICloudItem">
|
|
||||||
<string key="name">useICloudItem</string>
|
|
||||||
<string key="candidateClassName">NSMenuItem</string>
|
|
||||||
</object>
|
|
||||||
<object class="IBToOneOutletInfo" key="usersItem">
|
|
||||||
<string key="name">usersItem</string>
|
|
||||||
<string key="candidateClassName">NSMenuItem</string>
|
|
||||||
</object>
|
|
||||||
</dictionary>
|
|
||||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
|
||||||
<string key="majorKey">IBProjectSource</string>
|
|
||||||
<string key="minorKey">./Classes/MPAppDelegate.h</string>
|
|
||||||
</object>
|
|
||||||
</object>
|
|
||||||
<object class="IBPartialClassDescription">
|
|
||||||
<string key="className">MPAppDelegate_Shared</string>
|
|
||||||
<string key="superclassName">PearlAppDelegate</string>
|
|
||||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
|
||||||
<string key="majorKey">IBProjectSource</string>
|
|
||||||
<string key="minorKey">./Classes/MPAppDelegate_Shared.h</string>
|
|
||||||
</object>
|
|
||||||
</object>
|
|
||||||
</array>
|
|
||||||
</object>
|
</object>
|
||||||
|
<object class="IBClassDescriber" key="IBDocument.Classes"/>
|
||||||
<int key="IBDocument.localizationMode">0</int>
|
<int key="IBDocument.localizationMode">0</int>
|
||||||
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
|
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
|
||||||
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencies">
|
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencies">
|
||||||
|
Loading…
Reference in New Issue
Block a user