diff --git a/.travis.yml b/.travis.yml
index 3f8a35cb..69f79f06 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,6 +1,6 @@
language: objective-c
xcode_workspace: MasterPassword.xcworkspace
-xcode_scheme: MasterPassword iOS (Development)
+xcode_scheme: 'MasterPassword iOS (Development)'
xcode_sdk: iphonesimulator
git:
submodules: false
diff --git a/MasterPassword/ObjC/Mac/MPInitialWindow.xib b/MasterPassword/ObjC/Mac/MPInitialWindow.xib
index a13ff4c5..3aa11128 100644
--- a/MasterPassword/ObjC/Mac/MPInitialWindow.xib
+++ b/MasterPassword/ObjC/Mac/MPInitialWindow.xib
@@ -1,20 +1,22 @@
-
+
-
+
-
+
+
-
+
+
@@ -125,7 +127,7 @@ from anywhere.
-
+
@@ -153,4 +155,4 @@ from anywhere.
-
\ No newline at end of file
+
diff --git a/MasterPassword/ObjC/Mac/MPInitialWindowController.h b/MasterPassword/ObjC/Mac/MPInitialWindowController.h
new file mode 100644
index 00000000..57f4eaaa
--- /dev/null
+++ b/MasterPassword/ObjC/Mac/MPInitialWindowController.h
@@ -0,0 +1,29 @@
+/**
+ * Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
+ *
+ * See the enclosed file LICENSE for license information (LGPLv3). If you did
+ * not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * @author Maarten Billemont
+ * @license http://www.gnu.org/licenses/lgpl-3.0.txt
+ */
+
+//
+// MPInitialWindowController.h
+// MPInitialWindowController
+//
+// Created by lhunath on 2014-06-29.
+// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
+//
+
+#import
+
+@interface MPInitialWindowController : NSWindowController
+
+@property(nonatomic, weak) IBOutlet NSButton *openAtLoginButton;
+@property(nonatomic, weak) IBOutlet NSButton *enableCloudButton;
+
+- (IBAction)iphoneAppStore:(id)sender;
+- (IBAction)togglePreference:(id)sender;
+
+@end
diff --git a/MasterPassword/ObjC/Mac/MPInitialWindowController.m b/MasterPassword/ObjC/Mac/MPInitialWindowController.m
new file mode 100644
index 00000000..1f97239e
--- /dev/null
+++ b/MasterPassword/ObjC/Mac/MPInitialWindowController.m
@@ -0,0 +1,60 @@
+/**
+ * Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
+ *
+ * See the enclosed file LICENSE for license information (LGPLv3). If you did
+ * not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * @author Maarten Billemont
+ * @license http://www.gnu.org/licenses/lgpl-3.0.txt
+ */
+
+//
+// MPInitialWindowController.h
+// MPInitialWindowController
+//
+// Created by lhunath on 2014-06-29.
+// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
+//
+
+#import "MPInitialWindowController.h"
+#import "MPMacAppDelegate.h"
+#import "MPAppDelegate_Store.h"
+
+@implementation MPInitialWindowController
+
+#pragma mark - Life
+
+- (void)windowDidLoad {
+
+ [super windowDidLoad];
+
+ [[NSNotificationCenter defaultCenter] addObserverForName:NSWindowWillCloseNotification object:self.window
+ queue:nil usingBlock:^(NSNotification *note) {
+ [MPMacAppDelegate get].initialWindowController = nil;
+ }];
+}
+
+#pragma mark - Actions
+
+- (IBAction)iphoneAppStore:(id)sender {
+
+ [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://itunes.apple.com/app/id510296984"]];
+ [self close];
+}
+
+- (IBAction)togglePreference:(id)sender {
+
+ if (sender == self.enableCloudButton) {
+ if (([MPMacAppDelegate get].storeManager.cloudEnabled = self.enableCloudButton.state == NSOnState)) {
+ NSAlert *alert = [NSAlert new];
+ alert.messageText = @"iCloud Enabled";
+ alert.informativeText = @"If you already have a user on another iCloud-enabled device, "
+ @"it may take a moment for that user to sync down to this device.";
+ [alert runModal];
+ }
+ }
+ if (sender == self.openAtLoginButton)
+ [[MPMacAppDelegate get] setLoginItemEnabled:self.openAtLoginButton.state == NSOnState];
+}
+
+@end
diff --git a/MasterPassword/ObjC/Mac/MPMacAppDelegate.h b/MasterPassword/ObjC/Mac/MPMacAppDelegate.h
index 180920c0..08630db6 100644
--- a/MasterPassword/ObjC/Mac/MPMacAppDelegate.h
+++ b/MasterPassword/ObjC/Mac/MPMacAppDelegate.h
@@ -10,11 +10,13 @@
#import "MPAppDelegate_Shared.h"
#import "RHStatusItemView.h"
#import "MPPasswordWindowController.h"
+#import "MPInitialWindowController.h"
@interface MPMacAppDelegate : MPAppDelegate_Shared
@property(nonatomic, strong) RHStatusItemView *statusView;
-@property(nonatomic, strong) MPPasswordWindowController *passwordWindow;
+@property(nonatomic, strong) MPPasswordWindowController *passwordWindowController;
+@property(nonatomic, strong) MPInitialWindowController *initialWindowController;
@property(nonatomic, weak) IBOutlet NSMenuItem *lockItem;
@property(nonatomic, weak) IBOutlet NSMenuItem *showItem;
@property(nonatomic, strong) IBOutlet NSMenu *statusMenu;
@@ -26,17 +28,15 @@
@property(nonatomic, weak) IBOutlet NSMenuItem *createUserItem;
@property(nonatomic, weak) IBOutlet NSMenuItem *deleteUserItem;
@property(nonatomic, weak) IBOutlet NSMenuItem *usersItem;
-@property(nonatomic, weak) IBOutlet NSButton *openAtLoginButton;
-@property(nonatomic, weak) IBOutlet NSButton *enableCloudButton;
- (IBAction)showPasswordWindow:(id)sender;
+- (void)setLoginItemEnabled:(BOOL)enabled;
- (IBAction)togglePreference:(id)sender;
- (IBAction)newUser:(NSMenuItem *)sender;
- (IBAction)lock:(id)sender;
- (IBAction)rebuildCloud:(id)sender;
- (IBAction)corruptCloud:(id)sender;
- (IBAction)terminate:(id)sender;
-- (IBAction)iphoneAppStore:(id)sender;
- (IBAction)showPopup:(id)sender;
@end
diff --git a/MasterPassword/ObjC/Mac/MPMacAppDelegate.m b/MasterPassword/ObjC/Mac/MPMacAppDelegate.m
index 248f9c49..a8a41a6f 100644
--- a/MasterPassword/ObjC/Mac/MPMacAppDelegate.m
+++ b/MasterPassword/ObjC/Mac/MPMacAppDelegate.m
@@ -22,11 +22,6 @@
@end
-@interface MPMacAppDelegate()
-
-@property(nonatomic, strong) NSWindowController *initialWindow;
-@end
-
@implementation MPMacAppDelegate
#pragma clang diagnostic push
@@ -131,10 +126,9 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
err( @"Error registering 'lock' hotkey: %i", (int)status );
// Initial display.
- if ([[MPMacConfig get].firstRun boolValue]) {
- self.initialWindow = [[NSWindowController alloc] initWithWindowNibName:@"MPInitialWindow" owner:self];
- [self.initialWindow.window setLevel:NSFloatingWindowLevel];
- [self.initialWindow showWindow:self];
+ if ([[MPMacConfig get].firstRun boolValue]){
+ [(self.initialWindowController = [[MPInitialWindowController alloc] initWithWindowNibName:@"MPInitialWindow"]).window makeKeyAndOrderFront:self];
+ [NSApp activateIgnoringOtherApps:YES];
}
}
@@ -185,7 +179,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
}
self.openAtLoginItem.state = loginItemEnabled? NSOnState: NSOffState;
- self.openAtLoginButton.state = loginItemEnabled? NSOnState: NSOffState;
+ self.initialWindowController.openAtLoginButton.state = loginItemEnabled? NSOnState: NSOffState;
}
- (BOOL)loginItemEnabled {
@@ -320,23 +314,12 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
- (IBAction)togglePreference:(id)sender {
- if (sender == self.enableCloudButton) {
- if (([self storeManager].cloudEnabled = self.enableCloudButton.state == NSOnState)) {
- NSAlert *alert = [NSAlert new];
- alert.messageText = @"iCloud Enabled";
- alert.informativeText = @"If you already have a user on another iCloud-enabled device, "
- @"it may take a moment for that user to sync down to this device.";
- [alert runModal];
- }
- }
if (sender == self.useCloudItem)
[self storeManager].cloudEnabled = self.useCloudItem.state != NSOnState;
if (sender == self.hidePasswordsItem)
[MPConfig get].hidePasswords = [NSNumber numberWithBool:![[MPConfig get].hidePasswords boolValue]];
if (sender == self.rememberPasswordItem)
[MPConfig get].rememberLogin = [NSNumber numberWithBool:![[MPConfig get].rememberLogin boolValue]];
- if (sender == self.openAtLoginButton)
- [self setLoginItemEnabled:self.openAtLoginButton.state == NSOnState];
if (sender == self.openAtLoginItem)
[self setLoginItemEnabled:self.openAtLoginItem.state != NSOnState];
if (sender == self.savePasswordItem) {
@@ -432,20 +415,12 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
- (IBAction)terminate:(id)sender {
- [self.passwordWindow close];
- self.passwordWindow = nil;
+ [self.passwordWindowController close];
+ self.passwordWindowController = nil;
[NSApp terminate:nil];
}
-- (IBAction)iphoneAppStore:(id)sender {
-
- [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://itunes.apple.com/app/id510296984"]];
-
- [self.initialWindow close];
- self.initialWindow = nil;
-}
-
- (IBAction)showPopup:(id)sender {
[self.statusView popUpMenu];
@@ -466,12 +441,12 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
}
// Don't show window if we weren't already running (ie. if we haven't been activated before).
- PearlProfiler *profiler = [PearlProfiler profilerForTask:@"passwordWindow"];
- if (!self.passwordWindow)
- self.passwordWindow = [[MPPasswordWindowController alloc] initWithWindowNibName:@"MPPasswordWindowController"];
+ PearlProfiler *profiler = [PearlProfiler profilerForTask:@"passwordWindowController"];
+ if (!self.passwordWindowController)
+ self.passwordWindowController = [[MPPasswordWindowController alloc] initWithWindowNibName:@"MPPasswordWindowController"];
[profiler finishJob:@"init"];
- [self.passwordWindow showWindow:self];
+ [self.passwordWindowController showWindow:self];
[profiler finishJob:@"show"];
}
@@ -607,7 +582,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
- (void)updateMenuItems {
MPUserEntity *activeUser = [self activeUserForMainThread];
-// if (!(self.showItem.enabled = ![self.passwordWindow.window isVisible])) {
+// if (!(self.showItem.enabled = ![self.passwordWindowController.window isVisible])) {
// self.showItem.title = @"Show (Showing)";
// self.showItem.toolTip = @"Master Password is already showing.";
// }
@@ -633,7 +608,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
BOOL loginItemEnabled = [self loginItemEnabled];
self.openAtLoginItem.state = loginItemEnabled? NSOnState: NSOffState;
- self.openAtLoginButton.state = loginItemEnabled? NSOnState: NSOffState;
+ self.initialWindowController.openAtLoginButton.state = loginItemEnabled? NSOnState: NSOffState;
self.rememberPasswordItem.state = [[MPConfig get].rememberLogin boolValue]? NSOnState: NSOffState;
self.savePasswordItem.state = activeUser.saveKey? NSOnState: NSOffState;
@@ -654,7 +629,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
}
self.useCloudItem.state = self.storeManager.cloudEnabled? NSOnState: NSOffState;
- self.enableCloudButton.state = self.storeManager.cloudEnabled? NSOnState: NSOffState;
+ self.initialWindowController.enableCloudButton.state = self.storeManager.cloudEnabled? NSOnState: NSOffState;
self.useCloudItem.enabled = self.storeManager.cloudAvailable;
if (self.storeManager.cloudAvailable) {
self.useCloudItem.title = @"Use iCloud";
diff --git a/MasterPassword/ObjC/Mac/MPPasswordWindowController.h b/MasterPassword/ObjC/Mac/MPPasswordWindowController.h
index 81d73ca8..49159a7a 100644
--- a/MasterPassword/ObjC/Mac/MPPasswordWindowController.h
+++ b/MasterPassword/ObjC/Mac/MPPasswordWindowController.h
@@ -19,16 +19,21 @@
#import
#import "MPElementModel.h"
+@class MPMacAppDelegate;
+
@interface MPPasswordWindowController : NSWindowController
-@property(nonatomic, strong) NSMutableArray *elements;
+@property(nonatomic) NSMutableArray *elements;
+@property(nonatomic) NSString *masterPassword;
@property(nonatomic) BOOL alternatePressed;
+@property(nonatomic) BOOL locked;
@property(nonatomic, weak) IBOutlet NSArrayController *elementsController;
@property(nonatomic, weak) IBOutlet NSImageView *blurView;
@property(nonatomic, weak) IBOutlet NSTextField *inputLabel;
+@property(nonatomic, weak) IBOutlet NSTextField *securePasswordField;
+@property(nonatomic, weak) IBOutlet NSTextField *revealPasswordField;
@property(nonatomic, weak) IBOutlet NSSearchField *siteField;
-@property(nonatomic, weak) IBOutlet NSSecureTextField *passwordField;
@property(nonatomic, weak) IBOutlet NSTableView *siteTable;
@property(nonatomic, weak) IBOutlet NSProgressIndicator *progressView;
diff --git a/MasterPassword/ObjC/Mac/MPPasswordWindowController.m b/MasterPassword/ObjC/Mac/MPPasswordWindowController.m
index 3c6a3537..66abc447 100644
--- a/MasterPassword/ObjC/Mac/MPPasswordWindowController.m
+++ b/MasterPassword/ObjC/Mac/MPPasswordWindowController.m
@@ -25,6 +25,7 @@
#import "PearlProfiler.h"
#define MPAlertIncorrectMP @"MPAlertIncorrectMP"
+#define MPAlertChangeMP @"MPAlertChangeMP"
#define MPAlertCreateSite @"MPAlertCreateSite"
#define MPAlertChangeType @"MPAlertChangeType"
#define MPAlertChangeLogin @"MPAlertChangeLogin"
@@ -34,6 +35,7 @@
@interface MPPasswordWindowController()
@property(nonatomic, copy) NSString *currentSiteText;
+@property(nonatomic, strong) CAGradientLayer *siteGradient;
@end
@implementation MPPasswordWindowController { BOOL _skipTextChange; }
@@ -57,6 +59,12 @@
[self fadeIn];
[self updateUser];
}];
+ [[NSNotificationCenter defaultCenter] addObserverForName:NSWindowWillCloseNotification object:self.window
+ queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
+ NSWindow *sheet = [self.window attachedSheet];
+ if (sheet)
+ [NSApp endSheet:sheet];
+ }];
[[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationWillResignActiveNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
[self fadeOut];
@@ -69,20 +77,20 @@
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
[self updateUser];
}];
- [self.elementsController observeKeyPath:@"selection"
- withBlock:^(id from, id to, NSKeyValueChange cause, id _self) {
- [self updateSelection];
+ [self observeKeyPath:@"elementsController.selection"
+ withBlock:^(id from, id to, NSKeyValueChange cause, id _self) {
+ [_self updateSelection];
}];
NSSearchFieldCell *siteFieldCell = self.siteField.cell;
siteFieldCell.searchButtonCell = nil;
siteFieldCell.cancelButtonCell = nil;
- CAGradientLayer *gradient = [CAGradientLayer layer];
- gradient.colors = @[ (__bridge id)[NSColor whiteColor].CGColor, (__bridge id)[NSColor clearColor].CGColor ];
- gradient.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
- gradient.frame = self.siteTable.bounds;
- self.siteTable.superview.superview.layer.mask = gradient;
+ self.siteGradient = [CAGradientLayer layer];
+ self.siteGradient.colors = @[ (__bridge id)[NSColor whiteColor].CGColor, (__bridge id)[NSColor clearColor].CGColor ];
+ self.siteGradient.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
+ self.siteGradient.frame = self.siteTable.bounds;
+ self.siteTable.superview.superview.layer.mask = self.siteGradient;
}
- (void)flagsChanged:(NSEvent *)theEvent {
@@ -91,6 +99,14 @@
if (alternatePressed != self.alternatePressed) {
self.alternatePressed = alternatePressed;
[self.selectedElement updateContent];
+
+ if (self.locked) {
+ NSTextField *passwordField = self.securePasswordField;
+ if (self.securePasswordField.isHidden)
+ passwordField = self.revealPasswordField;
+ [passwordField becomeFirstResponder];
+ [[passwordField currentEditor] moveToEndOfLine:nil];
+ }
}
[super flagsChanged:theEvent];
@@ -107,29 +123,11 @@
- (BOOL)control:(NSControl *)control textView:(NSTextView *)fieldEditor doCommandBySelector:(SEL)commandSelector {
- if (control == self.passwordField) {
+ if (control == self.siteField) {
if (commandSelector == @selector( insertNewline: )) {
- NSString *password = self.passwordField.stringValue;
- [self.progressView startAnimation:self];
- [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
- MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserInContext:moc];
- NSString *userName = activeUser.name;
- BOOL success = [[MPMacAppDelegate get] signInAsUser:activeUser saveInContext:moc usingMasterPassword:password];
-
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- [self.progressView stopAnimation:self];
- if (!success)
- [[NSAlert alertWithError:[NSError errorWithDomain:MPErrorDomain code:0 userInfo:@{
- NSLocalizedDescriptionKey : PearlString( @"Incorrect master password for user %@", userName )
- }]] beginSheetModalForWindow:self.window modalDelegate:self
- didEndSelector:@selector( alertDidEnd:returnCode:contextInfo: ) contextInfo:MPAlertIncorrectMP];
- }];
- }];
+ [self useSite];
return YES;
}
- }
-
- if (control == self.siteField) {
if (commandSelector == @selector( moveUp: )) {
[self.elementsController selectPrevious:self];
return YES;
@@ -155,6 +153,26 @@
return YES;
}
+- (IBAction)doUnlockUser:(id)sender {
+
+ [self.progressView startAnimation:self];
+ [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
+ MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserInContext:moc];
+ NSString *userName = activeUser.name;
+ BOOL success = [[MPMacAppDelegate get] signInAsUser:activeUser saveInContext:moc usingMasterPassword:self.masterPassword];
+
+ PearlMainQueue( ^{
+ self.masterPassword = nil;
+ [self.progressView stopAnimation:self];
+ if (!success)
+ [[NSAlert alertWithError:[NSError errorWithDomain:MPErrorDomain code:0 userInfo:@{
+ NSLocalizedDescriptionKey : PearlString( @"Incorrect master password for user %@", userName )
+ }]] beginSheetModalForWindow:self.window modalDelegate:self
+ didEndSelector:@selector( alertDidEnd:returnCode:contextInfo: ) contextInfo:MPAlertIncorrectMP];
+ } );
+ }];
+}
+
- (IBAction)doSearchElements:(id)sender {
[self updateElements];
@@ -180,6 +198,34 @@
if (contextInfo == MPAlertIncorrectMP)
return;
+ if (contextInfo == MPAlertChangeMP) {
+ switch (returnCode) {
+ case NSAlertFirstButtonReturn: {
+ // "Reset" button.
+ [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
+ MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserInContext:context];
+ NSString *activeUserName = activeUser.name;
+ activeUser.keyID = nil;
+ [[MPMacAppDelegate get] forgetSavedKeyFor:activeUser];
+ [context saveToStore];
+
+ PearlMainQueue( ^{
+ NSAlert *alert_ = [NSAlert new];
+ alert_.messageText = @"Master Password Reset";
+ alert_.informativeText = strf( @"%@'s master password has been reset.\n\nYou can now set a new one by logging in.",
+ activeUserName );
+ [alert_ beginSheetModalForWindow:self.window modalDelegate:nil didEndSelector:NULL contextInfo:nil];
+
+ if ([MPMacAppDelegate get].key)
+ [[MPMacAppDelegate get] signOutAnimated:YES];
+ } );
+ }];
+ }
+ default:
+ break;
+ }
+ return;
+ }
if (contextInfo == MPAlertCreateSite) {
switch (returnCode) {
case NSAlertFirstButtonReturn: {
@@ -265,7 +311,7 @@
- (NSString *)query {
- return [self.siteField.stringValue stringByReplacingCharactersInRange:self.siteField.currentEditor.selectedRange withString:@""];
+ return [self.siteField.stringValue stringByReplacingCharactersInRange:self.siteField.currentEditor.selectedRange withString:@""]?: @"";
}
- (void)insertObject:(MPElementModel *)model inElementsAtIndex:(NSUInteger)index {
@@ -318,6 +364,21 @@
didEndSelector:@selector( alertDidEnd:returnCode:contextInfo: ) contextInfo:MPAlertChangeLogin];
}
+- (IBAction)resetMasterPassword:(id)sender {
+
+ MPUserEntity *activeUser = [MPMacAppDelegate get].activeUserForMainThread;
+
+ NSAlert *alert = [NSAlert new];
+ [alert addButtonWithTitle:@"Reset"];
+ [alert addButtonWithTitle:@"Cancel"];
+ [alert setMessageText:@"Reset My Master Password"];
+ [alert setInformativeText:strf( @"This will allow you to change %@'s master password.\n\n"
+ @"WARNING: All your site passwords will change. Do this only if you've forgotten your "
+ @"master password and are fully prepared to change all your sites' passwords to the new ones.", activeUser.name )];
+ [alert beginSheetModalForWindow:self.window modalDelegate:self
+ didEndSelector:@selector( alertDidEnd:returnCode:contextInfo: ) contextInfo:MPAlertChangeMP];
+}
+
- (IBAction)changePassword:(id)sender {
if (!self.selectedElement.stored)
@@ -367,10 +428,6 @@
- (BOOL)handleCommand:(SEL)commandSelector {
- if (commandSelector == @selector( insertNewline: )) {
- [self useSite];
- return YES;
- }
if (commandSelector == @selector( cancel: ) || commandSelector == @selector( cancelOperation: )) {
[self fadeOut];
return YES;
@@ -379,29 +436,52 @@
return NO;
}
+- (void)useSite {
+
+ MPElementModel *selectedElement = [self selectedElement];
+ if (selectedElement) {
+ // Performing action while content is available. Copy it.
+ [self copyContent:selectedElement.content];
+
+ [self fadeOut];
+
+ NSUserNotification *notification = [NSUserNotification new];
+ notification.title = @"Password Copied";
+ if (selectedElement.loginName.length)
+ notification.subtitle = PearlString( @"%@ at %@", selectedElement.loginName, selectedElement.siteName );
+ else
+ notification.subtitle = selectedElement.siteName;
+ [[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification];
+ }
+ else {
+ NSString *siteName = [self.siteField stringValue];
+ if ([siteName length])
+ // Performing action without content but a site name is written.
+ [self createNewSite:siteName];
+ }
+}
+
- (void)updateUser {
[MPMacAppDelegate managedObjectContextForMainThreadPerformBlock:^(NSManagedObjectContext *mainContext) {
- self.passwordField.hidden = YES;
- self.siteField.hidden = YES;
- self.siteTable.hidden = YES;
+ self.locked = YES;
self.inputLabel.stringValue = @"";
- self.passwordField.stringValue = @"";
self.siteField.stringValue = @"";
MPUserEntity *mainActiveUser = [[MPMacAppDelegate get] activeUserInContext:mainContext];
if (mainActiveUser) {
if ([MPMacAppDelegate get].key) {
self.inputLabel.stringValue = strf( @"%@'s password for:", mainActiveUser.name );
- self.siteField.hidden = NO;
- self.siteTable.hidden = NO;
+ self.locked = NO;
[self.siteField becomeFirstResponder];
}
else {
self.inputLabel.stringValue = strf( @"Enter %@'s master password:", mainActiveUser.name );
- self.passwordField.hidden = NO;
- [self.passwordField becomeFirstResponder];
+ NSTextField *passwordField = self.securePasswordField;
+ if (self.securePasswordField.isHidden)
+ passwordField = self.revealPasswordField;
+ [passwordField becomeFirstResponder];
}
}
@@ -464,33 +544,20 @@
NSMakeRange( siteNameQueryRange.length, siteName.length - siteNameQueryRange.length );
}
- NSRect selectedCellFrame = [self.siteTable frameOfCellAtColumn:0 row:((NSInteger)self.elementsController.selectionIndex)];
- [[(NSClipView *)self.siteTable.superview animator] setBoundsOrigin:selectedCellFrame.origin];
+ [self.siteTable scrollRowToVisible:(NSInteger)self.elementsController.selectionIndex];
+ [self updateGradient];
}
-- (void)useSite {
+- (void)updateGradient {
- MPElementModel *selectedElement = [self selectedElement];
- if (selectedElement) {
- // Performing action while content is available. Copy it.
- [self copyContent:selectedElement.content];
-
- [self fadeOut];
-
- NSUserNotification *notification = [NSUserNotification new];
- notification.title = @"Password Copied";
- if (selectedElement.loginName.length)
- notification.subtitle = PearlString( @"%@ at %@", selectedElement.loginName, selectedElement.siteName );
- else
- notification.subtitle = selectedElement.siteName;
- [[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification];
- }
- else {
- NSString *siteName = [self.siteField stringValue];
- if ([siteName length])
- // Performing action without content but a site name is written.
- [self createNewSite:siteName];
- }
+ NSView *siteScrollView = self.siteTable.superview.superview;
+ NSRect selectedCellFrame = [self.siteTable frameOfCellAtColumn:0 row:((NSInteger)self.elementsController.selectionIndex)];
+ CGFloat selectedOffset = [siteScrollView convertPoint:selectedCellFrame.origin fromView:self.siteTable].y;
+ CGFloat gradientOpacity = selectedOffset / siteScrollView.bounds.size.height;
+ self.siteGradient.colors = @[
+ (__bridge id)[NSColor whiteColor].CGColor,
+ (__bridge id)[NSColor colorWithDeviceWhite:1 alpha:gradientOpacity].CGColor
+ ];
}
- (void)createNewSite:(NSString *)siteName {
diff --git a/MasterPassword/ObjC/Mac/MPPasswordWindowController.xib b/MasterPassword/ObjC/Mac/MPPasswordWindowController.xib
index 45c31f49..c96c28c6 100644
--- a/MasterPassword/ObjC/Mac/MPPasswordWindowController.xib
+++ b/MasterPassword/ObjC/Mac/MPPasswordWindowController.xib
@@ -1,7 +1,8 @@
-
+
-
+
+
@@ -9,11 +10,12 @@
-
-
+
+
+
@@ -23,14 +25,14 @@
-
-
+
+
-
+
-
+
@@ -41,19 +43,19 @@
-
+
-
-
+
+
-
+
@@ -62,16 +64,89 @@
-
+
+
+
+
+
+
+
+
+ NSNegateBoolean
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ NSAllRomanInputSourcesLocaleIdentifier
+
+
+
+
+
+
+ NSNegateBoolean
+
+
+
+
+
+
+
+
+ NSNegateBoolean
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
+
@@ -158,9 +233,12 @@
+
+
+
-
+
@@ -172,7 +250,7 @@
-
+
@@ -180,13 +258,14 @@
-
+
-
+
+
@@ -284,7 +363,7 @@
-
+
-
+
-
+
@@ -347,8 +426,8 @@
-
-
+
+
@@ -361,25 +440,110 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ NSNegateBoolean
+
+
+
+
+
+
+
+
+
+
+
+
+ NSNegateBoolean
+
+
+
+
+
+
-
-
+
+
-
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ NSNegateBoolean
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
NSNegateBoolean
@@ -387,7 +551,7 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ NSNegateBoolean
+
+
+
+
+
+
+
+
+ NSNegateBoolean
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
@@ -540,6 +745,7 @@
+
@@ -558,18 +764,22 @@
+
+
+
+
@@ -577,14 +787,21 @@
+
+
+
+
+
+
+
@@ -596,14 +813,14 @@
-
+
-
+
-
+
@@ -648,13 +865,26 @@
+
+
+
+
+
+ "Personal password" allows you to store your own password. It cannot be regenerated in the event of loss. "Device private password" is similar but will never leave your device: it cannot be synced, backed up or exported.
+
+
+
+
+
+
-
+
+
diff --git a/MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/project.pbxproj b/MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/project.pbxproj
index e87d3574..491f4462 100644
--- a/MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/project.pbxproj
+++ b/MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/project.pbxproj
@@ -11,6 +11,7 @@
93D392EC39DA43C46C692C12 /* NSDictionary+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */; };
93D395F08A087F8A24689347 /* NSArray+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */; };
93D3970BCF85F7902E611168 /* PearlProfiler.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39DB3A8ADED08C39A6228 /* PearlProfiler.m */; };
+ 93D39784E725A34D1EE3FB3B /* MPInitialWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39D3CB30874147D9A9E1B /* MPInitialWindowController.m */; };
93D39C34FE35830EF5BE1D2A /* NSArray+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D396D04E57792A54D437AC /* NSArray+Indexing.h */; };
93D39C5789EFA607CF788082 /* MPElementModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39E73BF5CBF8E5B005CD3 /* MPElementModel.m */; };
93D39D304F73B3BBA031522A /* PearlProfiler.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D394EEFF5BF555A55AF361 /* PearlProfiler.h */; };
@@ -307,12 +308,14 @@
93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Indexing.m"; sourceTree = ""; };
93D39240B5143E01F0B75E96 /* MPElementModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementModel.h; sourceTree = ""; };
93D392C3918763B3B72CF366 /* MPPasswordWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordWindowController.h; sourceTree = ""; };
+ 93D39368EF3CBFEF2AFCA15A /* MPInitialWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPInitialWindowController.h; sourceTree = ""; };
93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Indexing.h"; sourceTree = ""; };
93D394EEFF5BF555A55AF361 /* PearlProfiler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PearlProfiler.h; path = ../../../External/Pearl/Pearl/PearlProfiler.h; sourceTree = ""; };
93D396D04E57792A54D437AC /* NSArray+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Indexing.h"; sourceTree = ""; };
93D3977484534E99F9BA579D /* MPPasswordWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordWindow.h; sourceTree = ""; };
93D39A57A7823DE98A0FF83C /* MPPasswordWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordWindowController.m; sourceTree = ""; };
93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+Indexing.m"; sourceTree = ""; };
+ 93D39D3CB30874147D9A9E1B /* MPInitialWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPInitialWindowController.m; sourceTree = ""; };
93D39D9D0061FF1159998F06 /* MPPasswordWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordWindow.m; sourceTree = ""; };
93D39DB3A8ADED08C39A6228 /* PearlProfiler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PearlProfiler.m; path = ../../../External/Pearl/Pearl/PearlProfiler.m; sourceTree = ""; };
93D39E73BF5CBF8E5B005CD3 /* MPElementModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementModel.m; sourceTree = ""; };
@@ -1210,6 +1213,8 @@
93D392C3918763B3B72CF366 /* MPPasswordWindowController.h */,
93D39D9D0061FF1159998F06 /* MPPasswordWindow.m */,
93D3977484534E99F9BA579D /* MPPasswordWindow.h */,
+ 93D39D3CB30874147D9A9E1B /* MPInitialWindowController.m */,
+ 93D39368EF3CBFEF2AFCA15A /* MPInitialWindowController.h */,
);
path = Mac;
sourceTree = "";
@@ -2466,6 +2471,7 @@
93D39C5789EFA607CF788082 /* MPElementModel.m in Sources */,
93D39F833DEC1C89B2F795AC /* MPPasswordWindowController.m in Sources */,
93D390C676DF52DA7E459F19 /* MPPasswordWindow.m in Sources */,
+ 93D39784E725A34D1EE3FB3B /* MPInitialWindowController.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
diff --git a/MasterPassword/Resources/Media/shot-laptop-leaning-iphone.png b/MasterPassword/Resources/Media/shot-laptop-leaning-iphone.png
index b1778a3e..b54b76f0 100644
Binary files a/MasterPassword/Resources/Media/shot-laptop-leaning-iphone.png and b/MasterPassword/Resources/Media/shot-laptop-leaning-iphone.png differ