Autotype for a selected entry. WIP

This commit is contained in:
michael starke
2014-12-16 20:37:55 +01:00
parent 1f92e5aeef
commit 3eb98a516c
9 changed files with 101 additions and 83 deletions

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="5056" systemVersion="13E28" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6254" systemVersion="13F34" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment defaultVersion="1080" identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="5056"/>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6254"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="MPAutotypeDaemon">
@@ -12,7 +12,7 @@
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window title="Autotype Selection" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" oneShot="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="1">
<windowStyleMask key="styleMask" titled="YES"/>
<rect key="contentRect" x="196" y="240" width="370" height="156"/>
@@ -23,7 +23,6 @@
<subviews>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="NdQ-vM-dHT">
<rect key="frame" x="123" y="13" width="82" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="WJJ-kW-fak">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
@@ -37,7 +36,6 @@ Gw
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Jlm-i9-jVy">
<rect key="frame" x="205" y="13" width="151" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Perform Autotype" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="AER-eU-kcu">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
@@ -46,12 +44,11 @@ DQ
</string>
</buttonCell>
<connections>
<action selector="executeAutotypeWithSelectedMatch:" target="-2" id="icM-Aj-OHO"/>
<action selector="performAutotypeWithSelectedMatch:" target="-2" id="vRo-HH-NIy"/>
</connections>
</button>
<popUpButton verticalHuggingPriority="750" horizontalCompressionResistancePriority="499" translatesAutoresizingMaskIntoConstraints="NO" id="tAw-72-pSm">
<rect key="frame" x="111" y="58" width="242" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" id="TAr-ZQ-aDu">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
@@ -64,7 +61,6 @@ DQ
</popUpButton>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="q1d-ED-T5M">
<rect key="frame" x="18" y="102" width="334" height="34"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" title="There are multiple matches for the current window. Please select which match should be used." id="ehp-xc-B5g">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
@@ -73,7 +69,6 @@ DQ
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Epz-xU-9TM">
<rect key="frame" x="18" y="64" width="89" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Match to use:" id="pLz-Kc-yPh">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>

View File

@@ -22,12 +22,15 @@
#import <Cocoa/Cocoa.h>
FOUNDATION_EXTERN NSString *const MPDidChangeStoredKeyFilesSettings;
APPKIT_EXTERN NSString *const MPDidChangeStoredKeyFilesSettings;
@class MPAutotypeDaemon;
@interface MPAppDelegate : NSObject <NSApplicationDelegate, NSMenuDelegate>
@property (strong) IBOutlet NSWindow *passwordCreatorWindow;
@property (strong) IBOutlet NSWindow *welcomeWindow;
@property (strong) MPAutotypeDaemon *autotypeDaemon;
@property (weak) IBOutlet NSMenuItem *saveMenuItem;
@property (nonatomic, assign) BOOL isAllowedToStoreKeyFile;
@@ -41,7 +44,6 @@ FOUNDATION_EXTERN NSString *const MPDidChangeStoredKeyFilesSettings;
*/
- (IBAction)clearRememberdKeyFiles:(id)sender;
- (NSString *)applicationName;
- (void)lockAllDocuments;

View File

@@ -44,7 +44,6 @@ NSString *const MPDidChangeStoredKeyFilesSettings = @"com.hicknhack.macpass.MPDi
@private
MPServerDaemon *serverDaemon;
MPLockDaemon *lockDaemon;
MPAutotypeDaemon *autotypeDaemon;
MPDockTileHelper *dockTileHelper;
BOOL _shouldOpenFile; // YES if app was started to open a
}
@@ -158,7 +157,7 @@ NSString *const MPDidChangeStoredKeyFilesSettings = @"com.hicknhack.macpass.MPDi
- (void)applicationDidFinishLaunching:(NSNotification *)notification {
serverDaemon = [[MPServerDaemon alloc] init];
lockDaemon = [[MPLockDaemon alloc] init];
autotypeDaemon = [[MPAutotypeDaemon alloc] init];
self.autotypeDaemon = [[MPAutotypeDaemon alloc] init];
//dockTileHelper = [[MPDockTileHelper alloc] init];
}

View File

@@ -9,6 +9,7 @@
#import <Foundation/Foundation.h>
@class DDHotKey;
@class KPKEntry;
/**
* The autotype daemon is repsonsible for registering the globa hotkey and to perform any autotype actions
@@ -19,7 +20,8 @@
@property (weak) IBOutlet NSPopUpButton *matchSelectionButton;
@property (readonly, strong) DDHotKey *registredHotKey;
- (IBAction)executeAutotypeWithSelectedMatch:(id)sender;
- (void)performAutotypeForEntry:(KPKEntry *)entryOrNil;
- (IBAction)performAutotypeWithSelectedMatch:(id)sender;
- (IBAction)cancelAutotypeSelection:(id)sender;
@end

View File

@@ -44,7 +44,7 @@ NSString *const kMPProcessIdentifierKey = @"kMPProcessIdentifierKey";
#pragma mark -
#pragma mark Lifecylce
- (id)init {
- (instancetype)init {
self = [super init];
if (self) {
_enabled = NO;
@@ -59,16 +59,17 @@ NSString *const kMPProcessIdentifierKey = @"kMPProcessIdentifierKey";
withKeyPath:[MPSettingsHelper defaultControllerPathForKey:kMPSettingsKeyGlobalAutotypeKeyDataKey]
options:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(_applicationWillBecomeActive:)
name:NSApplicationWillBecomeActiveNotification
object:[NSApplication sharedApplication]];
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
selector:@selector(_didDeactivateApplication:)
name:NSWorkspaceDidDeactivateApplicationNotification
object:nil];
}
return self;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
[self unbind:NSStringFromSelector(@selector(enabled))];
[self unbind:NSStringFromSelector(@selector(hotKeyData))];
}
@@ -93,11 +94,12 @@ NSString *const kMPProcessIdentifierKey = @"kMPProcessIdentifierKey";
}
}
- (void)executeAutotypeForEntry:(KPKEntry *)entry {
}
#pragma mark -
#pragma mark Actions
- (void)executeAutotypeWithSelectedMatch:(id)sender {
- (void)performAutotypeWithSelectedMatch:(id)sender {
NSMenuItem *item = [self.matchSelectionButton selectedItem];
MPAutotypeContext *context = [item representedObject];
[self.matchSelectionWindow orderOut:self];
@@ -107,7 +109,7 @@ NSString *const kMPProcessIdentifierKey = @"kMPProcessIdentifierKey";
- (void)cancelAutotypeSelection:(id)sender {
[self.matchSelectionWindow orderOut:sender];
if(self.targetPID) {
[MPAutotypeDaemon _orderApplicationToFront:self.targetPID];
[self _orderApplicationToFront:self.targetPID];
}
}
@@ -115,13 +117,11 @@ NSString *const kMPProcessIdentifierKey = @"kMPProcessIdentifierKey";
#pragma mark Hotkey evaluation
- (void)_didPressHotKey {
[self _performAutotypeUsingCurrentWindowAndApplication:YES];
[self _updateTargetInfoForFrontMostApplication];
[self performAutotypeForEntry:nil];
}
- (void)_performAutotypeUsingCurrentWindowAndApplication:(BOOL)useCurrentWindowAndApplication {
if(useCurrentWindowAndApplication) {
[self _updateTargetApplicationAndWindow];
}
- (void)performAutotypeForEntry:(KPKEntry *)entryOrNil {
NSInteger pid = [[NSProcessInfo processInfo] processIdentifier];
if(self.targetPID == pid) {
return; // We do not perform Autotype on ourselves
@@ -129,11 +129,16 @@ NSString *const kMPProcessIdentifierKey = @"kMPProcessIdentifierKey";
MPDocument *document = [self _findAutotypeDocument];
if(!document) {
return; // nothing to do
/* We do not have a document. This can be
a) there is none - nothing happens
b) there is at least one, but locked - we get called again after the document has been unlocked
*/
return;
}
MPAutotypeContext *context = [self _autotypeContextInDocument:document forWindowTitle:self.targetWindowTitle];
if(useCurrentWindowAndApplication) {
MPAutotypeContext *context = [self _autotypeContextInDocument:document forWindowTitle:self.targetWindowTitle preferredEntry:entryOrNil];
/* TODO: that's popping up if the mulit seleciton dialog goes up! */
if(!entryOrNil) {
NSImage *appIcon = [[NSApplication sharedApplication] applicationIconImage];
NSString *label = context ? NSLocalizedString(@"AUTOTYPE_OVERLAY_SINGLE_MATCH", "") : NSLocalizedString(@"AUTOTYPE_OVERLAY_NO_MATCH", "");
[[MPOverlayWindowController sharedController] displayOverlayImage:appIcon label:label atView:nil];
@@ -159,12 +164,12 @@ NSString *const kMPProcessIdentifierKey = @"kMPProcessIdentifierKey";
return currentDocument;
}
- (MPAutotypeContext *)_autotypeContextInDocument:(MPDocument *)document forWindowTitle:(NSString *)windowTitle {
- (MPAutotypeContext *)_autotypeContextInDocument:(MPDocument *)document forWindowTitle:(NSString *)windowTitle preferredEntry:(KPKEntry *)entry {
/*
Query the document to generate a autotype command list for the window title
We do not care where this came form, just get the autotype commands
*/
NSArray *autotypeCandidates = [document autotypContextsForWindowTitle:windowTitle];
NSArray *autotypeCandidates = [document autotypContextsForWindowTitle:windowTitle preferredEntry:entry];
NSUInteger candidates = [autotypeCandidates count];
if(candidates == 0) {
return nil;
@@ -180,13 +185,13 @@ NSString *const kMPProcessIdentifierKey = @"kMPProcessIdentifierKey";
if(nil == context) {
return; // No context to work with
}
if([self _orderApplicationToFront:self.targetPID]) {
/* Sleep a bit after the app was activated */
/* TODO - we can use a saver way and use a notification to chekc if the app actally was activated */
usleep(1 * NSEC_PER_MSEC);
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSArray *commands = [MPAutotypeCommand commandsForContext:context];
if([MPAutotypeDaemon _orderApplicationToFront:self.targetPID]) {
/* Sleep a bit after the app was activated */
usleep(0.5 * NSEC_PER_MSEC);
}
for(MPAutotypeCommand *command in commands) {
[command execute];
}
@@ -218,9 +223,7 @@ NSString *const kMPProcessIdentifierKey = @"kMPProcessIdentifierKey";
}
}
- (NSDictionary *)_frontMostApplicationInfoDict {
NSRunningApplication *frontApplication = [[NSWorkspace sharedWorkspace] frontmostApplication];
- (NSDictionary *)_infoDictionaryForApplication:(NSRunningApplication *)application {
NSArray *currentWindows = CFBridgingRelease(CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements, kCGNullWindowID));
for(NSDictionary *windowDict in currentWindows) {
NSString *windowTitle = windowDict[(NSString *)kCGWindowName];
@@ -228,7 +231,7 @@ NSString *const kMPProcessIdentifierKey = @"kMPProcessIdentifierKey";
continue;
}
NSNumber *processId = windowDict[(NSString *)kCGWindowOwnerPID];
if(processId && [processId isEqualToNumber:@(frontApplication.processIdentifier)]) {
if(processId && [processId isEqualToNumber:@(application.processIdentifier)]) {
return @{
kMPWindowTitleKey: windowDict[(NSString *)kCGWindowName],
kMPProcessIdentifierKey : processId
@@ -265,28 +268,28 @@ NSString *const kMPProcessIdentifierKey = @"kMPProcessIdentifierKey";
[self.matchSelectionButton setMenu:associationMenu];
[self.matchSelectionWindow makeKeyAndOrderFront:self];
[NSApp activateIgnoringOtherApps:YES];
/* Setup Items in Popup */
}
#pragma mark -
#pragma mark MPDocument Notifications
- (void)_didUnlockDatabase:(NSNotification *)notification {
/* Remove ourselves and call again to search matches */
[[NSNotificationCenter defaultCenter] removeObserver:self];
[self _performAutotypeUsingCurrentWindowAndApplication:NO];
[self performAutotypeForEntry:nil];
}
#pragma mark -
#pragma mark NSApplication Notifications
- (void)_applicationWillBecomeActive:(NSNotification *)notification {
//[self _updateTargetApplicationAndWindow];
//NSLog(@"_applicaiontWillBecomActive");
- (void)_didDeactivateApplication:(NSNotification *)notification {
NSDictionary *userInfo = notification.userInfo;
[self _updateTargeInformationForApplication:userInfo[NSWorkspaceApplicationKey]];
}
#pragma mark -
#pragma mark Application information
+ (BOOL)_orderApplicationToFront:(pid_t)processIdentifier {
- (BOOL)_orderApplicationToFront:(pid_t)processIdentifier {
NSRunningApplication *runingApplication = [NSRunningApplication runningApplicationWithProcessIdentifier:processIdentifier];
NSRunningApplication *frontApplication = [[NSWorkspace sharedWorkspace] frontmostApplication];
if(frontApplication.processIdentifier == processIdentifier) {
@@ -295,15 +298,20 @@ NSString *const kMPProcessIdentifierKey = @"kMPProcessIdentifierKey";
[runingApplication activateWithOptions:0];
return YES;
}
- (void)_updateTargetInfoForFrontMostApplication {
[self _updateTargeInformationForApplication:[[NSWorkspace sharedWorkspace] frontmostApplication]];
}
- (void)_updateTargetApplicationAndWindow {
/*
Determine the window title of the current front most application
Start searching the db for the best fit (based on title, then on window associations
*/
NSDictionary *frontApplicationInfoDict = [self _frontMostApplicationInfoDict];
self.targetPID = [frontApplicationInfoDict[kMPProcessIdentifierKey] intValue];
self.targetWindowTitle = frontApplicationInfoDict[kMPWindowTitleKey];
- (void)_updateTargeInformationForApplication:(NSRunningApplication *)application {
if(!application) {
self.targetPID = -1;
self.targetWindowTitle = @"";
}
else {
NSDictionary *frontApplicationInfoDict = [self _infoDictionaryForApplication:application];
self.targetPID = [frontApplicationInfoDict[kMPProcessIdentifierKey] intValue];
self.targetWindowTitle = frontApplicationInfoDict[kMPWindowTitleKey];
}
}
@end

View File

@@ -25,23 +25,25 @@
@interface MPDocument (Autotype)
/**
* Tests the given item for a possible wrong autotype format
* MacPass 0.4 and 0.4.1 did store wrong Autotype sequences and thus mangled database files
*
* @param item Item to test for malformation. Allowed Items are KPKNode, KPKEntry, KPKGroup and KPKAutotype
*
* @return YES if the given item is considered a possible candidate. NO in all other cases
*/
* Tests the given item for a possible wrong autotype format
* MacPass 0.4 and 0.4.1 did store wrong Autotype sequences and thus mangled database files
*
* @param item Item to test for malformation. Allowed Items are KPKNode, KPKEntry, KPKGroup and KPKAutotype
*
* @return YES if the given item is considered a possible candidate. NO in all other cases
*/
+ (BOOL)isCandidateForMalformedAutotype:(id)item;
/**
* Returns an NSArray containing all Autotype Contexts that match the given window title
* Returns an NSArray containing all Autotype Contexts that match the given window title.
* If no entry is set, all entries in the document will be searched
*
* @param windowTitle Window title to search matches for
* @param entry Entry to use for lookup. If nil lookup will be performed in complete document
*
* @return NSArray of MPAutotypeContexts for the given window title
* @return NSArray of MPAutotypeContext objects matching the window title.
*/
- (NSArray *)autotypContextsForWindowTitle:(NSString *)windowTitle;
- (NSArray *)autotypContextsForWindowTitle:(NSString *)windowTitle preferredEntry:(KPKEntry *)entryOrNil;
/**
* Checks if the document has malformed autotype items
*

View File

@@ -46,11 +46,11 @@
return (NSOrderedSame == [@"{TAB}{USERNAME}{TAB}{PASSWORD}{ENTER}" compare:keystrokeSequence options:NSCaseInsensitiveSearch]);
}
- (NSArray *)autotypContextsForWindowTitle:(NSString *)windowTitle {
- (NSArray *)autotypContextsForWindowTitle:(NSString *)windowTitle preferredEntry:(KPKEntry *)entry {
if(!windowTitle) {
return nil;
}
NSArray *autotypeEntries = [self.root autotypeableChildEntries];
NSArray *autotypeEntries = entry ? [[NSArray alloc] initWithObjects:entry, nil] : [self.root autotypeableChildEntries];
NSMutableArray *contexts = [[NSMutableArray alloc] initWithCapacity:MAX(1,ceil([autotypeEntries count] / 4.0))];
for(KPKEntry *entry in autotypeEntries) {
/* TODO:

View File

@@ -50,6 +50,8 @@
- (IBAction)pickExpiryDate:(id)sender;
- (IBAction)performAutotypeForEntry:(id)sender;
#pragma mark Helper
- (IBAction)fixAutotype:(id)sender;

View File

@@ -7,25 +7,26 @@
//
#import "MPDocumentWindowController.h"
#import "MPDocument.h"
#import "MPPasswordInputController.h"
#import "MPEntryViewController.h"
#import "MPToolbarDelegate.h"
#import "MPOutlineViewController.h"
#import "MPInspectorViewController.h"
#import "MPAppDelegate.h"
#import "MPActionHelper.h"
#import "MPDatabaseSettingsWindowController.h"
#import "MPPasswordEditWindowController.h"
#import "MPAppDelegate.h"
#import "MPAutotypeDaemon.h"
#import "MPConstants.h"
#import "MPSettingsHelper.h"
#import "MPDocumentWindowDelegate.h"
#import "MPFixAutotypeWindowController.h"
#import "MPContextToolbarButton.h"
#import "KPKTree.h"
#import "KPKEntry.h"
#import "MPDatabaseSettingsWindowController.h"
#import "MPDocument.h"
#import "MPDocumentWindowDelegate.h"
#import "MPEntryViewController.h"
#import "MPFixAutotypeWindowController.h"
#import "MPInspectorViewController.h"
#import "MPOutlineViewController.h"
#import "MPPasswordEditWindowController.h"
#import "MPPasswordInputController.h"
#import "MPSettingsHelper.h"
#import "MPToolbarDelegate.h"
#import "KPKCompositeKey.h"
#import "KPKEntry.h"
#import "KPKTree.h"
typedef NS_ENUM(NSUInteger, MPAlertContext) {
MPAlertLossySaveWarning,
@@ -369,6 +370,13 @@ typedef void (^MPPasswordChangedBlock)(BOOL didChangePassword);
[[NSUserDefaults standardUserDefaults] setBool:!inspectorWasVisible forKey:kMPSettingsKeyShowInspector];
}
- (void)performAutotypeForEntry:(id)sender {
id<MPTargetNodeResolving> entryResolver = [NSApp targetForAction:@selector(currentTargetEntry)];
KPKEntry *targetEntry = [entryResolver currentTargetEntry];
MPAutotypeDaemon *autotyped = ((MPAppDelegate *)[NSApplication sharedApplication]).autotypeDaemon;
[autotyped performAutotypeForEntry:targetEntry];
}
- (void)showInspector:(id)sender {
if(![self _isInspectorVisible]) {
[self toggleInspector:sender];