2
0

Mac support for preferences: rememberKey, saveKey, useICloud.

[ADDED]     OS X: Lots of warning checks when compiling.
[ADDED]     OS X: Ability to enable and disable iCloud.
[ADDED]     OS X: Support for rememberKey & saveKey.
[UPDATED]   storeKey -> saveKey: makes more sense to people.
[IMPROVED]  OS X: Password display window fancier.
This commit is contained in:
Maarten Billemont 2012-05-08 13:41:54 +02:00
parent 1b90c9bfa3
commit 7c5cea9c8d
13 changed files with 503 additions and 99 deletions

2
External/Pearl vendored

@ -1 +1 @@
Subproject commit a78cd7daf1fae0fc9e86702de9b9f6d5782ae352 Subproject commit a8346d6715eb258d11d41a4775e8f1abee620312

View File

@ -6093,7 +6093,12 @@
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_64_BIT)"; ARCHS = "$(ARCHS_STANDARD_64_BIT)";
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_CXX0X_EXTENSIONS = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES;
CLANG_WARN_OBJCPP_ARC_ABI = YES; CLANG_WARN_OBJCPP_ARC_ABI = YES;
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES;
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
GCC_C_LANGUAGE_STANDARD = gnu99; GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO; GCC_DYNAMIC_NO_PIC = NO;
@ -6104,10 +6109,32 @@
"$(inherited)", "$(inherited)",
); );
GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES;
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0; GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = YES;
GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = YES;
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
GCC_WARN_ABOUT_POINTER_SIGNEDNESS = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_ALLOW_INCOMPLETE_PROTOCOL = YES;
GCC_WARN_CHECK_SWITCH_STATEMENTS = YES;
GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES;
GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES;
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
GCC_WARN_MISSING_PARENTHESES = YES;
GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES;
GCC_WARN_SIGN_COMPARE = YES;
GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = NO;
GCC_WARN_UNKNOWN_PRAGMAS = YES;
GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_VALUE = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
LD_DYLIB_INSTALL_NAME = "@rpath/$(EXECUTABLE_PATH)"; LD_DYLIB_INSTALL_NAME = "@rpath/$(EXECUTABLE_PATH)";
LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks"; LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks";
@ -6125,15 +6152,42 @@
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_64_BIT)"; ARCHS = "$(ARCHS_STANDARD_64_BIT)";
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_CXX0X_EXTENSIONS = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES;
CLANG_WARN_OBJCPP_ARC_ABI = YES; CLANG_WARN_OBJCPP_ARC_ABI = YES;
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES;
COPY_PHASE_STRIP = YES; COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_C_LANGUAGE_STANDARD = gnu99; GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_ENABLE_OBJC_EXCEPTIONS = YES; GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES;
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0; GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = YES;
GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = YES;
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
GCC_WARN_ABOUT_POINTER_SIGNEDNESS = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_ALLOW_INCOMPLETE_PROTOCOL = YES;
GCC_WARN_CHECK_SWITCH_STATEMENTS = YES;
GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES;
GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES;
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
GCC_WARN_MISSING_PARENTHESES = YES;
GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES;
GCC_WARN_SIGN_COMPARE = YES;
GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNKNOWN_PRAGMAS = YES;
GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_VALUE = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
LD_DYLIB_INSTALL_NAME = "@rpath/$(EXECUTABLE_PATH)"; LD_DYLIB_INSTALL_NAME = "@rpath/$(EXECUTABLE_PATH)";
LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks"; LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks";
@ -6209,6 +6263,7 @@
DA79A9F5155821E500BAA07A /* Release */, DA79A9F5155821E500BAA07A /* Release */,
); );
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
}; };
DAB8D982150374AC00CED3BC /* Build configuration list for PBXProject "MasterPassword-Mac" */ = { DAB8D982150374AC00CED3BC /* Build configuration list for PBXProject "MasterPassword-Mac" */ = {
isa = XCConfigurationList; isa = XCConfigurationList;

View File

@ -17,7 +17,7 @@ static NSDictionary *keyQuery() {
static NSDictionary *MPKeyQuery = nil; static NSDictionary *MPKeyQuery = nil;
if (!MPKeyQuery) if (!MPKeyQuery)
MPKeyQuery = [PearlKeyChain createQueryForClass:kSecClassGenericPassword MPKeyQuery = [PearlKeyChain createQueryForClass:kSecClassGenericPassword
attributes:[NSDictionary dictionaryWithObject:@"Stored Master Password" attributes:[NSDictionary dictionaryWithObject:@"Saved Master Password"
forKey:(__bridge id)kSecAttrService] forKey:(__bridge id)kSecAttrService]
matches:nil]; matches:nil];
@ -38,7 +38,7 @@ static NSDictionary *keyHashQuery() {
- (void)forgetKey { - (void)forgetKey {
dbg(@"Deleting master key and hash from key chain."); dbg(@"Deleting key and hash from key chain.");
[PearlKeyChain deleteItemForQuery:keyQuery()]; [PearlKeyChain deleteItemForQuery:keyQuery()];
[PearlKeyChain deleteItemForQuery:keyHashQuery()]; [PearlKeyChain deleteItemForQuery:keyHashQuery()];
@ -55,15 +55,15 @@ static NSDictionary *keyHashQuery() {
- (void)loadStoredKey { - (void)loadStoredKey {
if ([[MPConfig get].storeKey boolValue]) { if ([[MPConfig get].saveKey boolValue]) {
// Key is stored in keychain. Load it. // Key is stored in keychain. Load it.
dbg(@"Loading key from key chain."); dbg(@"Loading key from key chain.");
[self updateKey:[PearlKeyChain dataOfItemForQuery:keyQuery()]]; [self updateKey:[PearlKeyChain dataOfItemForQuery:keyQuery()]];
dbg(@" -> Key %@.", self.key? @"found": @"NOT found"); dbg(@" -> Key %@.", self.key? @"found": @"NOT found");
} else { } else {
// Key should not be stored in keychain. Delete it. // Key should not be stored in keychain. Delete it.
dbg(@"Deleting key from key chain."); if ([PearlKeyChain deleteItemForQuery:keyQuery()] != errSecItemNotFound)
[PearlKeyChain deleteItemForQuery:keyQuery()]; dbg(@"Deleted key from key chain.");
#ifdef __IPHONE_OS_VERSION_MIN_REQUIRED #ifdef __IPHONE_OS_VERSION_MIN_REQUIRED
[TestFlight passCheckpoint:MPTestFlightCheckpointMPUnstored]; [TestFlight passCheckpoint:MPTestFlightCheckpointMPUnstored];
#endif #endif
@ -121,7 +121,7 @@ static NSDictionary *keyHashQuery() {
kSecAttrAccessibleWhenUnlocked, (__bridge id)kSecAttrAccessible, kSecAttrAccessibleWhenUnlocked, (__bridge id)kSecAttrAccessible,
#endif #endif
nil]]; nil]];
if ([[MPConfig get].storeKey boolValue]) { if ([[MPConfig get].saveKey boolValue]) {
dbg(@"Storing key in key chain."); dbg(@"Storing key in key chain.");
[PearlKeyChain addOrUpdateItemForQuery:keyQuery() [PearlKeyChain addOrUpdateItemForQuery:keyQuery()
withAttributes:[NSDictionary dictionaryWithObjectsAndKeys: withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:

View File

@ -109,12 +109,6 @@
return self.managedObjectContext; return self.managedObjectContext;
} }
- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager didSwitchToiCloud:(BOOL)didSwitch {
dbg(@"didSwitchToiCloud:%@", [NSNumber numberWithBool:didSwitch]);
//[masterViewController.iCloudSwitch setOn:didSwitch animated:YES];
}
- (void)saveContext { - (void)saveContext {
[self.managedObjectContext performBlock:^{ [self.managedObjectContext performBlock:^{

View File

@ -8,8 +8,7 @@
@interface MPConfig : PearlConfig @interface MPConfig : PearlConfig
@property (nonatomic, retain) NSNumber *dataStoreError; @property (nonatomic, retain) NSNumber *saveKey;
@property (nonatomic, retain) NSNumber *storeKey;
@property (nonatomic, retain) NSNumber *rememberKey; @property (nonatomic, retain) NSNumber *rememberKey;
+ (MPConfig *)get; + (MPConfig *)get;

View File

@ -7,21 +7,23 @@
// //
#import "MPConfig.h" #import "MPConfig.h"
#import "MPAppDelegate_Shared.h"
@implementation MPConfig @implementation MPConfig
@dynamic dataStoreError, storeKey, rememberKey; @dynamic saveKey, rememberKey;
- (id)init { - (id)init {
if(!(self = [super init])) if(!(self = [super init]))
return self; return nil;
[self.defaults registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys: [self.defaults registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:NO], NSStringFromSelector(@selector(dataStoreError)), [NSNumber numberWithBool:NO], NSStringFromSelector(@selector(saveKey)),
[NSNumber numberWithBool:NO], NSStringFromSelector(@selector(storeKey)),
[NSNumber numberWithBool:YES], NSStringFromSelector(@selector(rememberKey)), [NSNumber numberWithBool:YES], NSStringFromSelector(@selector(rememberKey)),
nil]]; nil]];
self.delegate = [MPAppDelegate get];
return self; return self;
} }

View File

@ -9,15 +9,17 @@
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#import "MPPasswordWindowController.h" #import "MPPasswordWindowController.h"
@interface MPAppDelegate : NSObject <NSApplicationDelegate> @interface MPAppDelegate : NSObject <NSApplicationDelegate, PearlConfigDelegate>
@property (strong) NSStatusItem *statusItem; @property (strong) NSStatusItem *statusItem;
@property (weak) IBOutlet NSMenuItem *unlockItem;
@property (weak) IBOutlet NSMenuItem *lockItem; @property (weak) IBOutlet NSMenuItem *lockItem;
@property (weak) IBOutlet NSMenuItem *showItem; @property (weak) IBOutlet NSMenuItem *showItem;
@property (strong) IBOutlet NSMenu *statusMenu; @property (strong) IBOutlet NSMenu *statusMenu;
@property (weak) IBOutlet NSMenuItem *useICloudItem;
@property (weak) IBOutlet NSMenuItem *rememberPasswordItem;
@property (weak) IBOutlet NSMenuItem *savePasswordItem;
- (IBAction)activate:(id)sender; - (IBAction)activate:(id)sender;
- (IBAction)unlock:(id)sender; - (IBAction)togglePreference:(NSMenuItem *)sender;
@end @end

View File

@ -20,16 +20,19 @@
@implementation MPAppDelegate @implementation MPAppDelegate
@synthesize statusItem; @synthesize statusItem;
@synthesize unlockItem;
@synthesize lockItem; @synthesize lockItem;
@synthesize showItem; @synthesize showItem;
@synthesize statusMenu; @synthesize statusMenu;
@synthesize useICloudItem;
@synthesize rememberPasswordItem;
@synthesize savePasswordItem;
@synthesize passwordWindow; @synthesize passwordWindow;
@synthesize key; @synthesize key;
@synthesize keyHash; @synthesize keyHash;
@synthesize keyHashHex; @synthesize keyHashHex;
#pragma GCC diagnostic ignored "-Wfour-char-constants"
static EventHotKeyID MPShowHotKey = { .signature = 'show', .id = 1 }; static EventHotKeyID MPShowHotKey = { .signature = 'show', .id = 1 };
+ (void)initialize { + (void)initialize {
@ -59,19 +62,41 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
- (void)showMenu { - (void)showMenu {
[self.showItem setEnabled:![self.passwordWindow.window isVisible]]; self.rememberPasswordItem.state = [[MPConfig get].rememberKey boolValue]? NSOnState: NSOffState;
self.savePasswordItem.state = [[MPConfig get].saveKey boolValue]? NSOnState: NSOffState;
self.showItem.enabled = ![self.passwordWindow.window isVisible];
[self.statusItem popUpStatusItemMenu:self.statusMenu]; [self.statusItem popUpStatusItemMenu:self.statusMenu];
} }
- (IBAction)activate:(id)sender { - (IBAction)activate:(id)sender {
if ([[NSApplication sharedApplication] isActive])
[self applicationDidBecomeActive:nil];
else
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES]; [[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
} }
- (IBAction)togglePreference:(NSMenuItem *)sender {
if (sender == useICloudItem)
[self.storeManager useiCloudStore:sender.state == NSOffState];
if (sender == rememberPasswordItem)
[MPConfig get].rememberKey = [NSNumber numberWithBool:![[MPConfig get].rememberKey boolValue]];
if (sender == savePasswordItem)
[MPConfig get].saveKey = [NSNumber numberWithBool:![[MPConfig get].saveKey boolValue]];
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Setup delegates and listeners.
[MPConfig get].delegate = self;
[self addObserver:self forKeyPath:@"key" options:0 context:nil]; [self addObserver:self forKeyPath:@"key" options:0 context:nil];
// Initially, use iCloud.
if ([[MPConfig get].firstRun boolValue])
[[self storeManager] useiCloudStore:YES];
// Status item. // Status item.
self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength]; self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength];
self.statusItem.title = @"•••"; self.statusItem.title = @"•••";
@ -89,17 +114,40 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
status = RegisterEventHotKey(35 /* p */, controlKey + cmdKey, MPShowHotKey, GetApplicationEventTarget(), 0, &hotKeyRef); status = RegisterEventHotKey(35 /* p */, controlKey + cmdKey, MPShowHotKey, GetApplicationEventTarget(), 0, &hotKeyRef);
if(status != noErr) if(status != noErr)
err(@"Error registering hotkey: %d", status); err(@"Error registering hotkey: %d", status);
}
[[self storeManager] useiCloudStore:YES]; - (void)applicationWillBecomeActive:(NSNotification *)notification {
if (!self.passwordWindow)
self.passwordWindow = [[MPPasswordWindowController alloc] initWithWindowNibName:@"MPPasswordWindowController"];
} }
- (void)applicationDidBecomeActive:(NSNotification *)notification { - (void)applicationDidBecomeActive:(NSNotification *)notification {
if (!self.passwordWindow) static BOOL firstTime = YES;
self.passwordWindow = [[MPPasswordWindowController alloc] initWithWindowNibName:@"MPPasswordWindowController"]; if (firstTime)
firstTime = NO;
else
[self.passwordWindow showWindow:self]; [self.passwordWindow showWindow:self];
}
[self unlock:self]; - (void)applicationWillResignActive:(NSNotification *)notification {
if (![[MPConfig get].rememberKey boolValue])
self.key = nil;
}
- (void)didUpdateConfigForKey:(SEL)configKey fromValue:(id)oldValue {
if (configKey == @selector(rememberKey))
self.rememberPasswordItem.state = [[MPConfig get].rememberKey boolValue]? NSOnState: NSOffState;
if (configKey == @selector(saveKey))
self.savePasswordItem.state = [[MPConfig get].saveKey boolValue]? NSOnState: NSOffState;
}
- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager didSwitchToiCloud:(BOOL)didSwitch {
self.useICloudItem.state = didSwitch? NSOnState: NSOffState;
} }
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
@ -114,54 +162,6 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
} }
} }
- (IBAction)unlock:(id)sender {
if (!self.key)
// Try and load the key from the keychain.
[self loadStoredKey];
if (!self.key)
// Ask the user to set the key through his master password.
dispatch_async(dispatch_get_main_queue(), ^{
if (self.key)
return;
NSAlert *alert = [NSAlert alertWithMessageText:@"Master Password is locked."
defaultButton:@"Unlock" alternateButton:@"Change" otherButton:@"Quit"
informativeTextWithFormat:@"Your master password is required to unlock the application."];
NSSecureTextField *passwordField = [[NSSecureTextField alloc] initWithFrame:NSMakeRect(0, 0, 200, 22)];
[alert setAccessoryView:passwordField];
[alert layout];
[passwordField becomeFirstResponder];
[alert beginSheetModalForWindow:self.passwordWindow.window modalDelegate:self
didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:NULL];
[self printStore];
});
}
- (void) alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo {
if (returnCode == NSAlertAlternateReturn)
// "Change" button.
if ([[NSAlert alertWithMessageText:@"Changing Master Password"
defaultButton:nil alternateButton:[PearlStrings get].commonButtonCancel otherButton:nil
informativeTextWithFormat:
@"This will allow you to log in with a different master password.\n\n"
@"Note that you will only see the sites and passwords for the master password you log in with.\n"
@"If you log in with a different master password, your current sites will be unavailable.\n\n"
@"You can always change back to your current master password later.\n"
@"Your current sites and passwords will then become available again."] runModal] == 1)
[self forgetKey];
if (returnCode == NSAlertOtherReturn)
// "Quit" button.
[[NSApplication sharedApplication] terminate:self];
if (![self tryMasterPassword:[(NSSecureTextField *)alert.accessoryView stringValue]])
[self unlock:self];
}
- (NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)window { - (NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)window {
return [[self managedObjectContext] undoManager]; return [[self managedObjectContext] undoManager];

View File

@ -8,10 +8,17 @@
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
@interface MPPasswordWindowController : NSWindowController <NSTextFieldDelegate> @interface MPPasswordWindowController : NSWindowController <NSTextFieldDelegate> {
NSString *_content;
}
@property (strong) NSString *content;
@property (weak) IBOutlet NSTextField *siteField; @property (weak) IBOutlet NSTextField *siteField;
@property (weak) IBOutlet NSTextField *contentField; @property (weak) IBOutlet NSTextField *contentField;
@property (weak) IBOutlet NSTextField *tipField; @property (weak) IBOutlet NSTextField *tipField;
- (void)unlock;
@end @end

View File

@ -26,11 +26,12 @@
- (void)windowDidLoad { - (void)windowDidLoad {
[self.contentField setStringValue:@""]; [self setContent:@""];
[self.tipField setStringValue:@""]; [self.tipField setStringValue:@""];
[[NSNotificationCenter defaultCenter] addObserverForName:NSWindowDidBecomeKeyNotification object:self.window queue:nil [[NSNotificationCenter defaultCenter] addObserverForName:NSWindowDidBecomeKeyNotification object:self.window queue:nil
usingBlock:^(NSNotification *note) { usingBlock:^(NSNotification *note) {
[self unlock];
[self.siteField selectText:self]; [self.siteField selectText:self];
}]; }];
[[NSNotificationCenter defaultCenter] addObserverForName:NSWindowWillCloseNotification object:self.window queue:nil [[NSNotificationCenter defaultCenter] addObserverForName:NSWindowWillCloseNotification object:self.window queue:nil
@ -53,6 +54,59 @@
[super windowDidLoad]; [super windowDidLoad];
} }
- (void)unlock {
if (![MPAppDelegate get].key)
// Try and load the key from the keychain.
[[MPAppDelegate get] loadStoredKey];
if (![MPAppDelegate get].key)
// Ask the user to set the key through his master password.
dispatch_async(dispatch_get_main_queue(), ^{
if ([MPAppDelegate get].key)
return;
NSAlert *alert = [NSAlert alertWithMessageText:@"Master Password is locked."
defaultButton:@"Unlock" alternateButton:@"Change" otherButton:@"Quit"
informativeTextWithFormat:@"Your master password is required to unlock the application."];
NSSecureTextField *passwordField = [[NSSecureTextField alloc] initWithFrame:NSMakeRect(0, 0, 200, 22)];
[alert setAccessoryView:passwordField];
[alert layout];
[passwordField becomeFirstResponder];
[alert beginSheetModalForWindow:self.window modalDelegate:self
didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:NULL];
});
}
- (void) alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo {
switch (returnCode) {
case NSAlertAlternateReturn:
// "Change" button.
if ([[NSAlert alertWithMessageText:@"Changing Master Password"
defaultButton:nil alternateButton:[PearlStrings get].commonButtonCancel otherButton:nil
informativeTextWithFormat:
@"This will allow you to log in with a different master password.\n\n"
@"Note that you will only see the sites and passwords for the master password you log in with.\n"
@"If you log in with a different master password, your current sites will be unavailable.\n\n"
@"You can always change back to your current master password later.\n"
@"Your current sites and passwords will then become available again."] runModal] == 1)
[[MPAppDelegate get] forgetKey];
break;
case NSAlertOtherReturn:
// "Quit" button.
[[NSApplication sharedApplication] terminate:self];
return;
case NSAlertDefaultReturn:
// "Unlock" button.
[[MPAppDelegate get] tryMasterPassword:[(NSSecureTextField *)alert.accessoryView stringValue]];
}
[self unlock];
}
- (NSArray *)control:(NSControl *)control textView:(NSTextView *)textView completions:(NSArray *)words forPartialWordRange:(NSRange)charRange indexOfSelectedItem:(NSInteger *)index { - (NSArray *)control:(NSControl *)control textView:(NSTextView *)textView completions:(NSArray *)words forPartialWordRange:(NSRange)charRange indexOfSelectedItem:(NSInteger *)index {
NSString *query = [[control stringValue] substringWithRange:charRange]; NSString *query = [[control stringValue] substringWithRange:charRange];
@ -83,20 +137,24 @@
- (BOOL)control:(NSControl *)control textView:(NSTextView *)fieldEditor doCommandBySelector:(SEL)commandSelector { - (BOOL)control:(NSControl *)control textView:(NSTextView *)fieldEditor doCommandBySelector:(SEL)commandSelector {
if (commandSelector == @selector(cancel:)) if (commandSelector == @selector(cancel:)) {
[self.window close]; [self.window close];
if (commandSelector == @selector(insertNewline:) && [[self.contentField stringValue] length]) { return YES;
}
if (commandSelector == @selector(insertNewline:) && [self.content length]) {
[[NSPasteboard generalPasteboard] declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil]; [[NSPasteboard generalPasteboard] declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
if ([[NSPasteboard generalPasteboard] setString:[self.contentField stringValue] forType:NSPasteboardTypeString]) { if ([[NSPasteboard generalPasteboard] setString:self.content forType:NSPasteboardTypeString]) {
self.tipField.alphaValue = 1; self.tipField.alphaValue = 1;
[self.tipField setStringValue:@"Copied!"]; [self.tipField setStringValue:@"Copied!"];
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 3.0f * NSEC_PER_SEC); dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0f * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^{ dispatch_after(popTime, dispatch_get_main_queue(), ^{
[NSAnimationContext beginGrouping]; [NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:0.2f]; [[NSAnimationContext currentContext] setDuration:0.2f];
[self.tipField.animator setAlphaValue:0]; [self.tipField.animator setAlphaValue:0];
[NSAnimationContext endGrouping]; [NSAnimationContext endGrouping];
}); });
[[self findElement] use];
return YES; return YES;
} else } else
wrn(@"Couldn't copy password to pasteboard."); wrn(@"Couldn't copy password to pasteboard.");
@ -111,21 +169,47 @@
[self trySite]; [self trySite];
} }
- (NSString *)content {
return _content;
}
- (void)setContent:(NSString *)content {
_content = content;
NSShadow *shadow = [NSShadow new];
shadow.shadowColor = [NSColor colorWithDeviceWhite:0.0f alpha:0.6f];
shadow.shadowOffset = NSMakeSize(1.0f, -1.0f);
shadow.shadowBlurRadius = 1.2f;
NSMutableParagraphStyle *paragraph = [NSMutableParagraphStyle new];
paragraph.alignment = NSCenterTextAlignment;
[self.contentField setAttributedStringValue:
[[NSAttributedString alloc] initWithString:_content
attributes:[[NSMutableDictionary alloc] initWithObjectsAndKeys:
shadow, NSShadowAttributeName,
paragraph, NSParagraphStyleAttributeName,
nil]]];
}
- (BOOL)trySite { - (BOOL)trySite {
MPElementEntity *result = [self findElement]; MPElementEntity *result = [self findElement];
if (!result) { if (!result) {
[self.contentField setStringValue:@""]; [self setContent:@""];
[self.tipField setStringValue:@""]; [self.tipField setStringValue:@""];
return NO; return NO;
} }
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];
[result use]; if (!description)
description = @"";
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
[self.contentField setStringValue:description]; [self setContent:description];
[self.tipField setStringValue:@"Hit enter to copy the password."]; [self.tipField setStringValue:@"Hit enter to copy the password."];
self.tipField.alphaValue = 1; self.tipField.alphaValue = 1;
}); });
@ -147,7 +231,7 @@
[element use]; [element use];
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
[self.contentField setStringValue:description? description: @""]; [self setContent:description];
}); });
}]; }];
*/ */

View File

@ -36,7 +36,7 @@
<string key="NSClassName">NSApplication</string> <string key="NSClassName">NSApplication</string>
</object> </object>
<object class="NSWindowTemplate" id="45434518"> <object class="NSWindowTemplate" id="45434518">
<int key="NSWindowStyleMask">8209</int> <int key="NSWindowStyleMask">277</int>
<int key="NSWindowBacking">2</int> <int key="NSWindowBacking">2</int>
<string key="NSWindowRect">{{600, 530}, {480, 159}}</string> <string key="NSWindowRect">{{600, 530}, {480, 159}}</string>
<int key="NSWTFlags">611845120</int> <int key="NSWTFlags">611845120</int>
@ -69,7 +69,7 @@
<double key="NSSize">13</double> <double key="NSSize">13</double>
<int key="NSfFlags">1044</int> <int key="NSfFlags">1044</int>
</object> </object>
<string key="NSPlaceholderString">Site</string> <string key="NSPlaceholderString">Site name</string>
<string key="NSCellIdentifier">_NS:9</string> <string key="NSCellIdentifier">_NS:9</string>
<reference key="NSControlView" ref="291791585"/> <reference key="NSControlView" ref="291791585"/>
<int key="NSTextBezelStyle">1</int> <int key="NSTextBezelStyle">1</int>
@ -115,6 +115,7 @@
</object> </object>
<string key="NSCellIdentifier">_NS:9</string> <string key="NSCellIdentifier">_NS:9</string>
<reference key="NSControlView" ref="226215720"/> <reference key="NSControlView" ref="226215720"/>
<bool key="NSDrawsBackground">YES</bool>
<object class="NSColor" key="NSBackgroundColor" id="245864165"> <object class="NSColor" key="NSBackgroundColor" id="245864165">
<int key="NSColorSpace">6</int> <int key="NSColorSpace">6</int>
<string key="NSCatalogName">System</string> <string key="NSCatalogName">System</string>
@ -165,8 +166,8 @@
<string key="NSReuseIdentifierKey">_NS:21</string> <string key="NSReuseIdentifierKey">_NS:21</string>
</object> </object>
<string key="NSScreenRect">{{0, 0}, {1680, 1028}}</string> <string key="NSScreenRect">{{0, 0}, {1680, 1028}}</string>
<string key="NSMinSize">{480, 153}</string> <string key="NSMinSize">{480, 150}</string>
<string key="NSMaxSize">{480, 339}</string> <string key="NSMaxSize">{480, 336}</string>
<bool key="NSWindowIsRestorable">YES</bool> <bool key="NSWindowIsRestorable">YES</bool>
</object> </object>
</array> </array>
@ -594,7 +595,7 @@
<boolean value="YES" key="22.IBNSWindowAutoPositionCentersHorizontal"/> <boolean value="YES" key="22.IBNSWindowAutoPositionCentersHorizontal"/>
<boolean value="YES" key="22.IBNSWindowAutoPositionCentersVertical"/> <boolean value="YES" key="22.IBNSWindowAutoPositionCentersVertical"/>
<string key="22.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="22.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES" key="22.NSWindowTemplate.visibleAtLaunch"/> <boolean value="NO" key="22.NSWindowTemplate.visibleAtLaunch"/>
<array class="NSMutableArray" key="23.IBNSViewMetadataConstraints"> <array class="NSMutableArray" key="23.IBNSViewMetadataConstraints">
<reference ref="137127436"/> <reference ref="137127436"/>
<reference ref="582018795"/> <reference ref="582018795"/>

View File

@ -61,6 +61,94 @@
<string key="NSResourceName">NSMenuMixedState</string> <string key="NSResourceName">NSMenuMixedState</string>
</object> </object>
</object> </object>
<object class="NSMenuItem" id="851296005">
<reference key="NSMenu" ref="764588027"/>
<string key="NSTitle">Preferences</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="552246001"/>
<reference key="NSMixedImage" ref="752047669"/>
<string key="NSAction">submenuAction:</string>
<object class="NSMenu" key="NSSubmenu" id="800575174">
<string key="NSTitle">Preferences</string>
<array class="NSMutableArray" key="NSMenuItems">
<object class="NSMenuItem" id="14397049">
<reference key="NSMenu" ref="800575174"/>
<string key="NSTitle">Use iCloud</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="552246001"/>
<reference key="NSMixedImage" ref="752047669"/>
</object>
<object class="NSMenuItem" id="461686112">
<reference key="NSMenu" ref="800575174"/>
<bool key="NSIsDisabled">YES</bool>
<string key="NSTitle">Synchronize available sites from your iCloud account.</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="552246001"/>
<reference key="NSMixedImage" ref="752047669"/>
<object class="NSAttributedString" key="NSAttributedTitle">
<string key="NSString">Synchronize available sites from your iCloud account.</string>
<dictionary key="NSAttributes" id="583461090">
<object class="NSFont" key="NSFont">
<string key="NSName">Helvetica</string>
<double key="NSSize">12</double>
<int key="NSfFlags">16</int>
</object>
<object class="NSParagraphStyle" key="NSParagraphStyle">
<int key="NSAlignment">4</int>
<nil key="NSTabStops"/>
</object>
</dictionary>
</object>
</object>
<object class="NSMenuItem" id="290760748">
<reference key="NSMenu" ref="800575174"/>
<string key="NSTitle">Remember Password</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="552246001"/>
<reference key="NSMixedImage" ref="752047669"/>
</object>
<object class="NSMenuItem" id="907921953">
<reference key="NSMenu" ref="800575174"/>
<bool key="NSIsDisabled">YES</bool>
<string key="NSTitle">Remember the password while the application is running.</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="552246001"/>
<reference key="NSMixedImage" ref="752047669"/>
<object class="NSAttributedString" key="NSAttributedTitle">
<string key="NSString">Remember the password while the application is running.</string>
<reference key="NSAttributes" ref="583461090"/>
</object>
</object>
<object class="NSMenuItem" id="110488020">
<reference key="NSMenu" ref="800575174"/>
<string key="NSTitle">Save Password</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="552246001"/>
<reference key="NSMixedImage" ref="752047669"/>
</object>
<object class="NSMenuItem" id="123831322">
<reference key="NSMenu" ref="800575174"/>
<bool key="NSIsDisabled">YES</bool>
<string key="NSTitle">Save the password in your keychain so you don't need to enter it again.</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="552246001"/>
<reference key="NSMixedImage" ref="752047669"/>
<object class="NSAttributedString" key="NSAttributedTitle">
<string key="NSString">Save the password in your keychain so you don't need to enter it again.</string>
<reference key="NSAttributes" ref="583461090"/>
</object>
</object>
</array>
<bool key="NSNoAutoenable">YES</bool>
</object>
</object>
<object class="NSMenuItem" id="466252869"> <object class="NSMenuItem" id="466252869">
<reference key="NSMenu" ref="764588027"/> <reference key="NSMenu" ref="764588027"/>
<bool key="NSIsDisabled">YES</bool> <bool key="NSIsDisabled">YES</bool>
@ -150,6 +238,54 @@
</object> </object>
<int key="connectionID">736</int> <int key="connectionID">736</int>
</object> </object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">useICloudItem</string>
<reference key="source" ref="976324537"/>
<reference key="destination" ref="14397049"/>
</object>
<int key="connectionID">749</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">rememberPasswordItem</string>
<reference key="source" ref="976324537"/>
<reference key="destination" ref="290760748"/>
</object>
<int key="connectionID">750</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">savePasswordItem</string>
<reference key="source" ref="976324537"/>
<reference key="destination" ref="110488020"/>
</object>
<int key="connectionID">751</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">togglePreference:</string>
<reference key="source" ref="976324537"/>
<reference key="destination" ref="14397049"/>
</object>
<int key="connectionID">752</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">togglePreference:</string>
<reference key="source" ref="976324537"/>
<reference key="destination" ref="290760748"/>
</object>
<int key="connectionID">753</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">togglePreference:</string>
<reference key="source" ref="976324537"/>
<reference key="destination" ref="110488020"/>
</object>
<int key="connectionID">754</int>
</object>
</array> </array>
<object class="IBMutableOrderedSet" key="objectRecords"> <object class="IBMutableOrderedSet" key="objectRecords">
<array key="orderedObjects"> <array key="orderedObjects">
@ -201,6 +337,7 @@
<reference ref="466252869"/> <reference ref="466252869"/>
<reference ref="846612332"/> <reference ref="846612332"/>
<reference ref="229948989"/> <reference ref="229948989"/>
<reference ref="851296005"/>
</array> </array>
<reference key="parent" ref="0"/> <reference key="parent" ref="0"/>
</object> </object>
@ -224,6 +361,57 @@
<reference key="object" ref="229948989"/> <reference key="object" ref="229948989"/>
<reference key="parent" ref="764588027"/> <reference key="parent" ref="764588027"/>
</object> </object>
<object class="IBObjectRecord">
<int key="objectID">739</int>
<reference key="object" ref="851296005"/>
<array class="NSMutableArray" key="children">
<reference ref="800575174"/>
</array>
<reference key="parent" ref="764588027"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">742</int>
<reference key="object" ref="800575174"/>
<array class="NSMutableArray" key="children">
<reference ref="14397049"/>
<reference ref="290760748"/>
<reference ref="907921953"/>
<reference ref="461686112"/>
<reference ref="110488020"/>
<reference ref="123831322"/>
</array>
<reference key="parent" ref="851296005"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">743</int>
<reference key="object" ref="14397049"/>
<reference key="parent" ref="800575174"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">745</int>
<reference key="object" ref="907921953"/>
<reference key="parent" ref="800575174"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">744</int>
<reference key="object" ref="290760748"/>
<reference key="parent" ref="800575174"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">746</int>
<reference key="object" ref="461686112"/>
<reference key="parent" ref="800575174"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">747</int>
<reference key="object" ref="110488020"/>
<reference key="parent" ref="800575174"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">748</int>
<reference key="object" ref="123831322"/>
<reference key="parent" ref="800575174"/>
</object>
</array> </array>
</object> </object>
<dictionary class="NSMutableDictionary" key="flattenedProperties"> <dictionary class="NSMutableDictionary" key="flattenedProperties">
@ -238,14 +426,86 @@
<string key="718.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="718.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="719.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="719.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="720.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="720.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="739.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="742.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="743.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="744.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="745.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="746.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="747.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="748.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
</dictionary> </dictionary>
<dictionary class="NSMutableDictionary" key="unlocalizedProperties"/> <dictionary class="NSMutableDictionary" key="unlocalizedProperties"/>
<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">736</int> <int key="maxID">754</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<array class="NSMutableArray" key="referencedPartialClassDescriptions">
<object class="IBPartialClassDescription">
<string key="className">MPAppDelegate</string>
<string key="superclassName">NSObject</string>
<dictionary class="NSMutableDictionary" key="actions">
<string key="activate:">id</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="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="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>
</dictionary>
<dictionary class="NSMutableDictionary" key="toOneOutletInfosByName">
<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>
</dictionary>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">./Classes/MPAppDelegate.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">

View File

@ -160,7 +160,7 @@
[[NSNotificationCenter defaultCenter] addObserverForName:kIASKAppSettingChanged object:nil queue:nil [[NSNotificationCenter defaultCenter] addObserverForName:kIASKAppSettingChanged object:nil queue:nil
usingBlock:^(NSNotification *note) { usingBlock:^(NSNotification *note) {
if ([NSStringFromSelector(@selector(storeKey)) if ([NSStringFromSelector(@selector(saveKey))
isEqualToString:[note.object description]]) { isEqualToString:[note.object description]]) {
[self updateKey:self.key]; [self updateKey:self.key];
[self loadKey:YES]; [self loadKey:YES];