diff --git a/MacPass.xcodeproj/project.pbxproj b/MacPass.xcodeproj/project.pbxproj index fe100822..c1e056d0 100644 --- a/MacPass.xcodeproj/project.pbxproj +++ b/MacPass.xcodeproj/project.pbxproj @@ -73,7 +73,6 @@ 4C25D58716CF0FAA00F6806C /* EntryView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4C25D58616CF0FAA00F6806C /* EntryView.xib */; }; 4C2671AD17A7D8FC00F3A645 /* HNHColorWell.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C2671AC17A7D8FC00F3A645 /* HNHColorWell.m */; }; 4C26C33F18D8C92100CF1A1C /* MPTemporaryFileStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C26C33E18D8C92100CF1A1C /* MPTemporaryFileStorage.m */; }; - 4C26C34718D8C9C400CF1A1C /* KPKBinary+Quickook.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C26C34618D8C9C400CF1A1C /* KPKBinary+Quickook.m */; }; 4C26C34B18D8D5A300CF1A1C /* MPPreviewViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C26C34918D8D5A300CF1A1C /* MPPreviewViewController.m */; }; 4C26C34C18D8D5A300CF1A1C /* PreviewView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4C26C34A18D8D5A300CF1A1C /* PreviewView.xib */; }; 4C2724D71778FF1A00FD8456 /* NSUUID+KeePassKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C2724D61778FF1A00FD8456 /* NSUUID+KeePassKit.m */; }; @@ -205,6 +204,7 @@ 4C888C9016EB6C91003D34A1 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 4C888C8E16EB6C91003D34A1 /* Localizable.strings */; }; 4C888C9316EB6F5E003D34A1 /* MPToolbarItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C888C9216EB6F5E003D34A1 /* MPToolbarItem.m */; }; 4C888C9716EB754B003D34A1 /* MPActionHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C888C9616EB754B003D34A1 /* MPActionHelper.m */; }; + 4C88C66918D9F8D600F43852 /* MPTemporaryFileStorageCenter.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C88C66818D9F8D600F43852 /* MPTemporaryFileStorageCenter.m */; }; 4C89F521182F9FDD0069C73C /* NSString+Commands.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C89F520182F9FDD0069C73C /* NSString+Commands.m */; }; 4C89F524182FB4740069C73C /* MPAutotypeCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C89F523182FB4740069C73C /* MPAutotypeCommand.m */; }; 4C8A173D1790AA41008B5C17 /* NSData+Keyfile.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C8A173C1790AA41008B5C17 /* NSData+Keyfile.m */; }; @@ -487,8 +487,6 @@ 4C2671AC17A7D8FC00F3A645 /* HNHColorWell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HNHColorWell.m; sourceTree = ""; }; 4C26C33D18D8C92100CF1A1C /* MPTemporaryFileStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPTemporaryFileStorage.h; sourceTree = ""; }; 4C26C33E18D8C92100CF1A1C /* MPTemporaryFileStorage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPTemporaryFileStorage.m; sourceTree = ""; }; - 4C26C34518D8C9C400CF1A1C /* KPKBinary+Quickook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "KPKBinary+Quickook.h"; sourceTree = ""; }; - 4C26C34618D8C9C400CF1A1C /* KPKBinary+Quickook.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "KPKBinary+Quickook.m"; sourceTree = ""; }; 4C26C34818D8D5A300CF1A1C /* MPPreviewViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPreviewViewController.h; sourceTree = ""; }; 4C26C34918D8D5A300CF1A1C /* MPPreviewViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPreviewViewController.m; sourceTree = ""; }; 4C26C34A18D8D5A300CF1A1C /* PreviewView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PreviewView.xib; sourceTree = ""; }; @@ -718,6 +716,8 @@ 4C888C9216EB6F5E003D34A1 /* MPToolbarItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPToolbarItem.m; sourceTree = ""; }; 4C888C9516EB754B003D34A1 /* MPActionHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPActionHelper.h; sourceTree = ""; }; 4C888C9616EB754B003D34A1 /* MPActionHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPActionHelper.m; sourceTree = ""; }; + 4C88C66718D9F8D600F43852 /* MPTemporaryFileStorageCenter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPTemporaryFileStorageCenter.h; sourceTree = ""; }; + 4C88C66818D9F8D600F43852 /* MPTemporaryFileStorageCenter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPTemporaryFileStorageCenter.m; sourceTree = ""; }; 4C89F51F182F9FDD0069C73C /* NSString+Commands.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+Commands.h"; sourceTree = ""; }; 4C89F520182F9FDD0069C73C /* NSString+Commands.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+Commands.m"; sourceTree = ""; }; 4C89F522182FB4740069C73C /* MPAutotypeCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAutotypeCommand.h; sourceTree = ""; }; @@ -997,8 +997,6 @@ 4CC6DB7917D23719002C6091 /* KPKNode+IconImage.m */, 4CEED1C417D7BD0E007180F1 /* NSError+Messages.h */, 4CEED1C517D7BD0E007180F1 /* NSError+Messages.m */, - 4C26C34518D8C9C400CF1A1C /* KPKBinary+Quickook.h */, - 4C26C34618D8C9C400CF1A1C /* KPKBinary+Quickook.m */, ); name = Categories; sourceTree = ""; @@ -1210,6 +1208,8 @@ 4C3C4EAE18D7039300153127 /* MPValueTransformerHelper.m */, 4C26C33D18D8C92100CF1A1C /* MPTemporaryFileStorage.h */, 4C26C33E18D8C92100CF1A1C /* MPTemporaryFileStorage.m */, + 4C88C66718D9F8D600F43852 /* MPTemporaryFileStorageCenter.h */, + 4C88C66818D9F8D600F43852 /* MPTemporaryFileStorageCenter.m */, ); name = Helper; sourceTree = ""; @@ -2217,6 +2217,7 @@ 4C245B77176E1E3D0086100E /* DDNumber.m in Sources */, 4C245B78176E1E3D0086100E /* DDRange.m in Sources */, 4C3C4EA618D6FEA100153127 /* TTTJSONTransformer.m in Sources */, + 4C88C66918D9F8D600F43852 /* MPTemporaryFileStorageCenter.m in Sources */, 4C245B79176E1E3D0086100E /* HTTPAuthenticationRequest.m in Sources */, 4C245B7A176E1E3D0086100E /* HTTPConnection.m in Sources */, 4C245B7B176E1E3D0086100E /* HTTPMessage.m in Sources */, @@ -2265,7 +2266,6 @@ 4C10412C178CDD44001B5239 /* NSDate+Humanized.m in Sources */, 4CD3ABBA178F71B50073F5C5 /* KPKTree.m in Sources */, 4CD3ABBF178F72610073F5C5 /* KPKEntry.m in Sources */, - 4C26C34718D8C9C400CF1A1C /* KPKBinary+Quickook.m in Sources */, 4CD3ABC2178F72720073F5C5 /* KPKGroup.m in Sources */, 4C591B57178F897A0080B16B /* KPKBinary.m in Sources */, 4C0C59F118B17F10009C7B76 /* DDHotKeyUtilities.m in Sources */, diff --git a/MacPass/Base.lproj/MainMenu.xib b/MacPass/Base.lproj/MainMenu.xib index 40048b00..147b1d34 100644 --- a/MacPass/Base.lproj/MainMenu.xib +++ b/MacPass/Base.lproj/MainMenu.xib @@ -112,6 +112,11 @@ + + + + + diff --git a/MacPass/EntryInspectorView.xib b/MacPass/EntryInspectorView.xib index bd1671dc..d3516454 100644 --- a/MacPass/EntryInspectorView.xib +++ b/MacPass/EntryInspectorView.xib @@ -168,7 +168,7 @@ - + @@ -176,47 +176,44 @@ - - + + + + + + + + + + + + + + - - - - - + + + + - - @@ -880,7 +877,6 @@ CA - diff --git a/MacPass/IntegrationSettings.xib b/MacPass/IntegrationSettings.xib index cfeaff77..f38d333b 100644 --- a/MacPass/IntegrationSettings.xib +++ b/MacPass/IntegrationSettings.xib @@ -1,13 +1,14 @@ - + - + + @@ -15,11 +16,11 @@ - + - + @@ -44,7 +45,7 @@ - + @@ -90,17 +91,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + - \ No newline at end of file + diff --git a/MacPass/KPKBinary+Quickook.h b/MacPass/KPKBinary+Quickook.h deleted file mode 100644 index d9c5af73..00000000 --- a/MacPass/KPKBinary+Quickook.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// KPKBinary+Quickook.h -// MacPass -// -// Created by Michael Starke on 18/03/14. -// Copyright (c) 2014 HicknHack Software GmbH. All rights reserved. -// - -#import "KPKBinary.h" - -@interface KPKBinary (Quickook) - -- (void)quicklock; - -@end diff --git a/MacPass/KPKBinary+Quickook.m b/MacPass/KPKBinary+Quickook.m deleted file mode 100644 index 826be3f0..00000000 --- a/MacPass/KPKBinary+Quickook.m +++ /dev/null @@ -1,17 +0,0 @@ -// -// KPKBinary+Quickook.m -// MacPass -// -// Created by Michael Starke on 18/03/14. -// Copyright (c) 2014 HicknHack Software GmbH. All rights reserved. -// - -#import "KPKBinary+Quickook.h" -#import "MPTemporaryFileStorage.h" -@implementation KPKBinary (Quickook) - -- (void)quicklock { - -} - -@end diff --git a/MacPass/MPActionHelper.h b/MacPass/MPActionHelper.h index 25ae8642..38cae57c 100644 --- a/MacPass/MPActionHelper.h +++ b/MacPass/MPActionHelper.h @@ -26,7 +26,8 @@ typedef NS_ENUM(NSUInteger, MPActionType) { MPActionDatabaseSettings, // Show the settings for the database MPActionEditTemplateGroup, // Edit the Template group MPActionExportXML, // Exporte as XML - MPActionImportXML // Import form XML + MPActionImportXML, // Import form XML + MPActionToggleQuicklook }; /** * Helper to retrieve commonly used actions diff --git a/MacPass/MPActionHelper.m b/MacPass/MPActionHelper.m index 2b8c98e6..3c5b4d41 100644 --- a/MacPass/MPActionHelper.m +++ b/MacPass/MPActionHelper.m @@ -30,8 +30,9 @@ @(MPActionEmptyTrash): @"emptyTrash:", @(MPActionDatabaseSettings): @"showDatabaseSettings:", @(MPActionEditTemplateGroup): @"editTemplateGroup:", - @(MPActionExportXML): @"exportAsXML", - @(MPActionImportXML): @"importFromXMl", + @(MPActionExportXML): @"exportAsXML:", + @(MPActionImportXML): @"importFromXMl:", + @(MPActionToggleQuicklook): @"toggleQuicklookPreview:", }; }); return actionDict; diff --git a/MacPass/MPAppDelegate.m b/MacPass/MPAppDelegate.m index 206540b2..bfb45aee 100644 --- a/MacPass/MPAppDelegate.m +++ b/MacPass/MPAppDelegate.m @@ -32,6 +32,8 @@ #import "MPAutotypeDaemon.h" #import "MPDocumentWindowController.h" +#import "MPTemporaryFileStorageCenter.h" + #import "MPDocument.h" #import "KPKCompositeKey.h" @@ -123,6 +125,17 @@ NSString *const MPDidChangeStoredKeyFilesSettings = @"com.hicknhack.macpass.MPDi } +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender { + if([[MPTemporaryFileStorageCenter defaultCenter] hasPendingStorages]) { + dispatch_async(dispatch_get_main_queue(), ^{ + [[MPTemporaryFileStorageCenter defaultCenter] cleanupStorages]; + [sender replyToApplicationShouldTerminate:YES]; + }); + return NSTerminateLater; + } + return NSTerminateNow; +} + - (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename { _shouldOpenFile = YES; NSURL *fileURL = [NSURL fileURLWithPath:filename]; diff --git a/MacPass/MPAttachmentTableViewDelegate.m b/MacPass/MPAttachmentTableViewDelegate.m index 23c98b8d..349e4547 100644 --- a/MacPass/MPAttachmentTableViewDelegate.m +++ b/MacPass/MPAttachmentTableViewDelegate.m @@ -48,13 +48,7 @@ NSTableCellView *view; if([selectedIndexes containsIndex:row]) { MPSelectedAttachmentTableCellView *cellView = [tableView makeViewWithIdentifier:@"SelectedCell" owner:tableView]; - [cellView.saveButton setTag:row]; - [cellView.saveButton setAction:@selector(saveAttachment:)]; - [cellView.saveButton setTarget:self.viewController]; - [cellView.removeButton setTag:row]; - [cellView.removeButton setAction:@selector(removeAttachment:)]; - [cellView.removeButton setTarget:nil]; - [cellView.removeButton setTarget:self.viewController]; + [cellView.actionButton setMenu:[self allocateActionMenu]]; view = cellView; } else { @@ -76,4 +70,25 @@ return view; } +- (NSMenu *)allocateActionMenu { + NSMenu *menu = [[NSMenu alloc] init]; + /* Image for Popup button */ + NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:@"" action:NULL keyEquivalent:@""]; + [item setImage:[NSImage imageNamed:NSImageNameActionTemplate]]; + [menu addItem:item]; + /* Quicklook */ + [menu addItemWithTitle:NSLocalizedString(@"PREVIEW", "") action:@selector(toggleQuicklookPreview:) keyEquivalent:@""]; + /* Save */ + item = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"SAVE", "") action:@selector(saveAttachment:) keyEquivalent:@""]; + [item setTarget:self.viewController]; + [menu addItem:item]; + /* Remove */ + [menu addItem:[NSMenuItem separatorItem]]; + item = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"DELETE", "") action:@selector(removeAttachment:) keyEquivalent:@""]; + [item setTarget:self.viewController]; + [menu addItem:item]; + + return menu; +} + @end diff --git a/MacPass/MPConstants.h b/MacPass/MPConstants.h index a20c094b..7e1e0360 100644 --- a/MacPass/MPConstants.h +++ b/MacPass/MPConstants.h @@ -12,7 +12,6 @@ #import FOUNDATION_EXPORT NSString *const MPPasteBoardType; -FOUNDATION_EXPORT NSString *const MPErrorDomain; FOUNDATION_EXPORT NSString *const MPLegacyDocumentUTI; FOUNDATION_EXPORT NSString *const MPXMLDocumentUTI; diff --git a/MacPass/MPConstants.m b/MacPass/MPConstants.m index ba724976..fab37b1f 100644 --- a/MacPass/MPConstants.m +++ b/MacPass/MPConstants.m @@ -9,6 +9,5 @@ #import "MPConstants.h" NSString *const MPPasteBoardType = @"com.hicknhack.macpass.pasteboard"; -NSString *const MPErrorDomain = @"com.hicknhack.macpass.error"; NSString *const MPLegacyDocumentUTI = @"com.hicknhack.macpass.kdb"; NSString *const MPXMLDocumentUTI = @"com.hicknhack.macpass.kdbx"; \ No newline at end of file diff --git a/MacPass/MPDocument.m b/MacPass/MPDocument.m index fa737827..fa06c996 100644 --- a/MacPass/MPDocument.m +++ b/MacPass/MPDocument.m @@ -43,6 +43,8 @@ #import "KPKTimeInfo.h" #import "KPKAttribute.h" +#import "NSError+Messages.h" + NSString *const MPDocumentDidAddGroupNotification = @"com.hicknhack.macpass.MPDocumentDidAddGroupNotification"; NSString *const MPDocumentDidRevertNotifiation = @"com.hicknhack.macpass.MPDocumentDidRevertNotifiation"; diff --git a/MacPass/MPDocumentWindowController.h b/MacPass/MPDocumentWindowController.h index 3cd5df47..76507bca 100644 --- a/MacPass/MPDocumentWindowController.h +++ b/MacPass/MPDocumentWindowController.h @@ -17,7 +17,7 @@ @class MPOutlineViewController; @class MPToolbarDelegate; -@interface MPDocumentWindowController : NSWindowController +@interface MPDocumentWindowController : NSWindowController @property (readonly, strong) MPPasswordInputController *passwordInputController; @property (readonly, strong) MPEntryViewController *entryViewController; diff --git a/MacPass/MPDocumentWindowController.m b/MacPass/MPDocumentWindowController.m index 92436401..ffa6521d 100644 --- a/MacPass/MPDocumentWindowController.m +++ b/MacPass/MPDocumentWindowController.m @@ -51,8 +51,6 @@ typedef void (^MPPasswordChangedBlock)(void); @property (nonatomic, copy) MPPasswordChangedBlock passwordChangedBlock; -@property (strong) QLPreviewPanel *previewPanel; - @end @implementation MPDocumentWindowController @@ -439,38 +437,4 @@ typedef void (^MPPasswordChangedBlock)(void); } -- (void)toggleQuicklookPreview:(id)sender { - if([QLPreviewPanel sharedPreviewPanelExists] && [[QLPreviewPanel sharedPreviewPanel] isVisible]) { - [[QLPreviewPanel sharedPreviewPanel] orderOut:sender]; - } - else { - [[QLPreviewPanel sharedPreviewPanel] makeKeyAndOrderFront:sender]; - } -} - -#pragma mark - -#pragma mark QLPreviewDelegate -- (BOOL)acceptsPreviewPanelControl:(QLPreviewPanel *)panel { - return YES; -} -- (void)beginPreviewPanelControl:(QLPreviewPanel *)panel { - self.previewPanel = panel; - [self.previewPanel setDataSource:self]; -} - -- (void)endPreviewPanelControl:(QLPreviewPanel *)panel { - self.previewPanel = nil; -} - -#pragma mark - -#pragma mark QLPreviewDataSource - -- (NSInteger)numberOfPreviewItemsInPreviewPanel:(QLPreviewPanel *)panel { - return (self.previewPanel == panel ? 1 : 0); -} - -- (id)previewPanel:(QLPreviewPanel *)panel previewItemAtIndex:(NSInteger)index { - return [[NSURL alloc] initWithString:@"file:///test.txt"]; -} - @end diff --git a/MacPass/MPEntryInspectorViewController.h b/MacPass/MPEntryInspectorViewController.h index 53b9060d..64282979 100644 --- a/MacPass/MPEntryInspectorViewController.h +++ b/MacPass/MPEntryInspectorViewController.h @@ -8,10 +8,12 @@ #import "MPViewController.h" +#import + @class HNHRoundedSecureTextField; @class MPDocument; -@interface MPEntryInspectorViewController : MPViewController +@interface MPEntryInspectorViewController : MPViewController @property (weak) IBOutlet NSTextField *titleTextField; @property (weak) IBOutlet NSTextField *usernameTextField; @@ -57,7 +59,10 @@ - (IBAction)addWindowAssociation:(id)sender; - (IBAction)removeWindowAssociation:(id)sender; +- (IBAction)toggleQuicklookPreview:(id)sender; + - (void)beginEditing; - (void)endEditing; + @end diff --git a/MacPass/MPEntryInspectorViewController.m b/MacPass/MPEntryInspectorViewController.m index 28087531..006e3aa2 100644 --- a/MacPass/MPEntryInspectorViewController.m +++ b/MacPass/MPEntryInspectorViewController.m @@ -16,6 +16,10 @@ #import "MPDocument.h" #import "MPIconHelper.h" #import "MPValueTransformerHelper.h" +#import "MPTemporaryFileStorage.h" +#import "MPTemporaryFileStorageCenter.h" +#import "MPActionHelper.h" +#import "MPSettingsHelper.h" #import "KPKEntry.h" #import "KPKBinary.h" @@ -49,6 +53,7 @@ typedef NS_ENUM(NSUInteger, MPEntryTab) { @property (strong) NSPopover *activePopover; @property (nonatomic, weak) KPKEntry *entry; +@property (strong) MPTemporaryFileStorage *quicklookStorage; @end @@ -128,7 +133,11 @@ typedef NS_ENUM(NSUInteger, MPEntryTab) { } - (void)saveAttachment:(id)sender { - KPKBinary *binary = self.entry.binaries[[sender tag]]; + NSInteger row = [self.attachmentTableView selectedRow]; + if(row < 0) { + return; // No selection + } + KPKBinary *binary = self.entry.binaries[row]; NSSavePanel *savePanel = [NSSavePanel savePanel]; [savePanel setCanCreateDirectories:YES]; [savePanel setNameFieldStringValue:binary.name]; @@ -156,7 +165,11 @@ typedef NS_ENUM(NSUInteger, MPEntryTab) { } - (void)removeAttachment:(id)sender { - KPKBinary *binary = self.entry.binaries[[sender tag]]; + NSInteger row = [self.attachmentTableView selectedRow]; + if(row < 0) { + return; // no selection + } + KPKBinary *binary = self.entry.binaries[row]; [self.entry removeBinary:binary]; } @@ -172,7 +185,22 @@ typedef NS_ENUM(NSUInteger, MPEntryTab) { } } -#pragma mark Editing +- (void)toggleQuicklookPreview:(id)sender { + if([QLPreviewPanel sharedPreviewPanelExists] && [[QLPreviewPanel sharedPreviewPanel] isVisible]) { + QLPreviewPanel *panel = [QLPreviewPanel sharedPreviewPanel]; + if([self acceptsPreviewPanelControl:nil]) { + [self _updatePreviewItemForPanel:panel]; + [panel reloadData]; + } + else { + [panel orderOut:sender]; + } + } + else { + [[QLPreviewPanel sharedPreviewPanel] makeKeyAndOrderFront:sender]; + } +} + - (void)beginEditing { [self _toggleEditing:YES]; @@ -181,6 +209,45 @@ typedef NS_ENUM(NSUInteger, MPEntryTab) { [self _toggleEditing:NO]; } +- (BOOL)validateMenuItem:(NSMenuItem *)menuItem { + switch([MPActionHelper typeForAction:[menuItem action]]) { + case MPActionToggleQuicklook: { + BOOL enabled = [[NSUserDefaults standardUserDefaults] boolForKey:kMPSettingsKeyEnableQuicklookPreview]; + return enabled ? [self acceptsPreviewPanelControl:nil] : NO; + } + default: + return YES; + } +} + +#pragma mark - +#pragma mark QLPreviewPanelDelegate + +- (BOOL)acceptsPreviewPanelControl:(QLPreviewPanel *)panel { + if(self.activeTab == MPEntryTabFiles) { + return ([self.attachmentTableView selectedRow] != -1); + } + return NO; +} + +- (void)beginPreviewPanelControl:(QLPreviewPanel *)panel { + [self _updatePreviewItemForPanel:panel]; +} + +- (void)endPreviewPanelControl:(QLPreviewPanel *)panel { + MPTemporaryFileStorage *storage = (MPTemporaryFileStorage *)panel.dataSource; + [[MPTemporaryFileStorageCenter defaultCenter] unregisterStorage:storage]; +} + +- (void)_updatePreviewItemForPanel:(QLPreviewPanel *)panel { + NSInteger row = [self.attachmentTableView selectedRow]; + NSAssert(row > -1, @"Row needs to be selected"); + KPKBinary *binary = self.entry.binaries[row]; + MPTemporaryFileStorage *oldStorage = (MPTemporaryFileStorage *)panel.dataSource; + [[MPTemporaryFileStorageCenter defaultCenter] unregisterStorage:oldStorage]; + panel.dataSource = [[MPTemporaryFileStorageCenter defaultCenter] storageForBinary:binary]; +} + #pragma mark - #pragma mark Popovers diff --git a/MacPass/MPIntegrationSettingsController.h b/MacPass/MPIntegrationSettingsController.h index 5268a805..170d8e24 100644 --- a/MacPass/MPIntegrationSettingsController.h +++ b/MacPass/MPIntegrationSettingsController.h @@ -13,5 +13,6 @@ @property (weak) IBOutlet NSButton *enableServerCheckbutton; @property (weak) IBOutlet NSButton *enableGlobalAutotypeCheckbutton; +@property (weak) IBOutlet NSButton *enableQuicklookCheckbutton; @end diff --git a/MacPass/MPIntegrationSettingsController.m b/MacPass/MPIntegrationSettingsController.m index 0460c813..b3b6da63 100644 --- a/MacPass/MPIntegrationSettingsController.m +++ b/MacPass/MPIntegrationSettingsController.m @@ -37,10 +37,12 @@ NSUserDefaultsController *defaultsController = [NSUserDefaultsController sharedUserDefaultsController]; NSString *serverKeyPath = [MPSettingsHelper defaultControllerPathForKey:kMPSettingsKeyEnableHttpServer]; NSString *globalAutotypeKeyPath = [MPSettingsHelper defaultControllerPathForKey:kMPSettingsKeyEnableGlobalAutotype]; + NSString *quicklookKeyPath = [MPSettingsHelper defaultControllerPathForKey:kMPSettingsKeyEnableQuicklookPreview]; [self.enableServerCheckbutton bind:NSValueBinding toObject:defaultsController withKeyPath:serverKeyPath options:nil]; [self.enableServerCheckbutton setEnabled:NO]; [self.enableGlobalAutotypeCheckbutton bind:NSValueBinding toObject:defaultsController withKeyPath:globalAutotypeKeyPath options:nil]; [self.enableGlobalAutotypeCheckbutton setEnabled:NO]; + [self.enableQuicklookCheckbutton bind:NSValueBinding toObject:defaultsController withKeyPath:quicklookKeyPath options:nil]; } @end diff --git a/MacPass/MPSelectedAttachmentTableCellView.h b/MacPass/MPSelectedAttachmentTableCellView.h index df6b142a..69827cfe 100644 --- a/MacPass/MPSelectedAttachmentTableCellView.h +++ b/MacPass/MPSelectedAttachmentTableCellView.h @@ -11,7 +11,6 @@ /* Simple View with an additional Button to add an Action to selected rows */ @interface MPSelectedAttachmentTableCellView : NSTableCellView -@property (nonatomic, weak) IBOutlet NSButton *saveButton; -@property (nonatomic, weak) IBOutlet NSButton *removeButton; +@property (nonatomic, weak) IBOutlet NSPopUpButton *actionButton; @end diff --git a/MacPass/MPSelectedAttachmentTableCellView.m b/MacPass/MPSelectedAttachmentTableCellView.m index 4493ba3a..899a96ff 100644 --- a/MacPass/MPSelectedAttachmentTableCellView.m +++ b/MacPass/MPSelectedAttachmentTableCellView.m @@ -10,4 +10,5 @@ @implementation MPSelectedAttachmentTableCellView + @end diff --git a/MacPass/MPTemporaryFileStorage.h b/MacPass/MPTemporaryFileStorage.h index 98b0ff8c..90549684 100644 --- a/MacPass/MPTemporaryFileStorage.h +++ b/MacPass/MPTemporaryFileStorage.h @@ -7,17 +7,17 @@ // #import -/** - * Instance to handle a temporary file storage. Quicklook support uses this as a means to vent attachments to the system - * After using the file, the storage is removed. - */ +#import @class KPKBinary; - -@interface MPTemporaryFileStorage : NSObject +/** + * File Storage the Storage center vents on request. Use this to feed as datasource to QLPreviewPanels + */ +@interface MPTemporaryFileStorage : NSObject - (instancetype)initWithBinary:(KPKBinary *)binary; -- (void)quicklook; +- (void)cleanup; +- (void)cleanupNow; -@end +@end \ No newline at end of file diff --git a/MacPass/MPTemporaryFileStorage.m b/MacPass/MPTemporaryFileStorage.m index 44b9cfa0..7e9f5ed6 100644 --- a/MacPass/MPTemporaryFileStorage.m +++ b/MacPass/MPTemporaryFileStorage.m @@ -7,13 +7,17 @@ // #import "MPTemporaryFileStorage.h" +#import "MPTemporaryFileStorageCenter.h" + #import "KPKBinary.h" -#import +#import "NSError+Messages.h" @interface MPTemporaryFileStorage () @property (strong) KPKBinary *binary; +@property (assign) BOOL loadScheduled; +@property (strong) NSURL *temporaryFileURL; @end @@ -23,28 +27,103 @@ self = [super init]; if(self) { _binary = binary; + _loadScheduled = NO; + [[MPTemporaryFileStorageCenter defaultCenter] registerStorage:self]; } return self; } -- (void)quicklook { - - NSString *fileName = [NSString stringWithFormat:@"%@_%@", [[NSProcessInfo processInfo] globallyUniqueString], self.binary.name]; - NSURL *fileURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:fileName]]; - - NSError *error; - BOOL success = [self.binary.data writeToURL:fileURL options:0 error:&error]; - if(!success) { - if(error) { - [NSApp presentError:error]; - } - return; - } +- (void)dealloc { + NSLog(@"dealloc"); + [self cleanup]; + [[MPTemporaryFileStorageCenter defaultCenter] unregisterStorage:self]; +} - NSTask *task = [[NSTask alloc] init]; - [task setLaunchPath:@"srm"]; - [task setArguments:@[@"-m", fileName]]; +- (void)cleanupNow { + [self _cleanupBinary:YES]; +} + +- (void)cleanup { + [self _cleanupBinary:NO]; +} + +#pragma mark - +#pragma mark QLPreviewPanelDataSource + +- (id)previewPanel:(QLPreviewPanel *)panel previewItemAtIndex:(NSInteger)index { + if(!self.temporaryFileURL && !self.loadScheduled) { + self.loadScheduled = YES; + dispatch_queue_t defaultQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + dispatch_async(defaultQueue, ^{ + BOOL success = [self _saveBinary:self.binary]; + if(success){ + dispatch_async(dispatch_get_main_queue(), ^{ + [panel refreshCurrentPreviewItem]; + }); + } + }); + } + return self; +} + +- (NSInteger)numberOfPreviewItemsInPreviewPanel:(QLPreviewPanel *)panel { + return 1; +} + +#pragma mark - +#pragma mark QLPreviewItem + +- (NSURL *)previewItemURL { + return self.temporaryFileURL; +} + +- (NSString *)previewItemTitle { + return self.binary.name; +} + +#pragma mark - +#pragma mark Private + +- (BOOL)_saveBinary:(KPKBinary *)binary { + if(!binary || !binary.data || !binary.name || [binary.name length] == 0) { + return NO; + } + NSString *fileName = [NSString stringWithFormat:@"%@_%@", [[NSProcessInfo processInfo] globallyUniqueString], binary.name]; + self.temporaryFileURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:fileName]]; + + BOOL success = [binary.data writeToURL:self.temporaryFileURL options:0 error:0]; + if(!success) { + self.temporaryFileURL = nil; + return NO; + } + NSLog(@"Did write data to %@", [self.temporaryFileURL path]); + return YES; } +- (void)_cleanupBinary:(BOOL)blockUntilDone { + if(!self.temporaryFileURL) { + return; // No URL to clean up + } + NSString *path = [self.temporaryFileURL path]; + if(blockUntilDone) { + [MPTemporaryFileStorage _runCleanupForPath:path]; + } + else { + dispatch_queue_t lowQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0); + dispatch_async(lowQueue, ^{ + [MPTemporaryFileStorage _runCleanupForPath:path]; + }); + } + self.temporaryFileURL = nil; +} + ++ (void)_runCleanupForPath:(NSString *)path { + NSTask *task = [[NSTask alloc] init]; + [task setLaunchPath:@"/usr/bin/srm"]; + [task setArguments:@[@"-m", path]]; + [task launch]; + [task waitUntilExit]; +} + @end diff --git a/MacPass/MPTemporaryFileStorageCenter.h b/MacPass/MPTemporaryFileStorageCenter.h new file mode 100644 index 00000000..c4f78304 --- /dev/null +++ b/MacPass/MPTemporaryFileStorageCenter.h @@ -0,0 +1,25 @@ +// +// MPTemporaryFileStorageCleaner.h +// MacPass +// +// Created by Michael Starke on 19/03/14. +// Copyright (c) 2014 HicknHack Software GmbH. All rights reserved. +// + +#import + +@class MPTemporaryFileStorage; +@class KPKBinary; + +@interface MPTemporaryFileStorageCenter : NSObject + ++ (instancetype)defaultCenter; + +- (BOOL)hasPendingStorages; + +- (MPTemporaryFileStorage *)storageForBinary:(KPKBinary *)binary; +- (void)registerStorage:(MPTemporaryFileStorage *)storage; +- (void)unregisterStorage:(MPTemporaryFileStorage *)storage; +- (void)cleanupStorages; + +@end diff --git a/MacPass/MPTemporaryFileStorageCenter.m b/MacPass/MPTemporaryFileStorageCenter.m new file mode 100644 index 00000000..1f5aef8c --- /dev/null +++ b/MacPass/MPTemporaryFileStorageCenter.m @@ -0,0 +1,61 @@ +// +// MPTemporaryFileStorageCleaner.m +// MacPass +// +// Created by Michael Starke on 19/03/14. +// Copyright (c) 2014 HicknHack Software GmbH. All rights reserved. +// + +#import "MPTemporaryFileStorageCenter.h" +#import "MPTemporaryFileStorage.h" + +@interface MPTemporaryFileStorageCenter () { + NSMutableArray *_storages; +} + +@end + +@implementation MPTemporaryFileStorageCenter + ++ (instancetype)defaultCenter { + static MPTemporaryFileStorageCenter *instance; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + instance = [[MPTemporaryFileStorageCenter alloc] init]; + }); + return instance; +} + +- (instancetype)init { + self = [super init]; + if(self) { + _storages = [[NSMutableArray alloc] init]; + } + return self; +} + +- (BOOL)hasPendingStorages { + return [_storages count] > 0; +} + +- (MPTemporaryFileStorage *)storageForBinary:(KPKBinary *)binary { + return [[MPTemporaryFileStorage alloc] initWithBinary:binary]; +} + +- (void)cleanupStorages { + for(MPTemporaryFileStorage *storage in _storages) { + [storage cleanupNow]; + } + _storages = nil; +} + +- (void)registerStorage:(MPTemporaryFileStorage *)storage { + [_storages addObject:storage]; +} + +- (void)unregisterStorage:(MPTemporaryFileStorage *)storage { + [storage cleanup]; + [_storages removeObject:storage]; +} + +@end diff --git a/MacPass/NSError+Messages.h b/MacPass/NSError+Messages.h index b49eaa53..241e0485 100644 --- a/MacPass/NSError+Messages.h +++ b/MacPass/NSError+Messages.h @@ -8,8 +8,12 @@ #import +FOUNDATION_EXPORT NSString *const MPErrorDomain; + @interface NSError (Messages) ++ (NSError *)errorWithCode:(NSInteger)code description:(NSString *)description; + - (NSString *)descriptionForErrorCode; @end diff --git a/MacPass/NSError+Messages.m b/MacPass/NSError+Messages.m index 773763d6..5ef6e63c 100644 --- a/MacPass/NSError+Messages.m +++ b/MacPass/NSError+Messages.m @@ -7,11 +7,16 @@ // #import "NSError+Messages.h" -#import "KPKErrors.h" + +NSString *const MPErrorDomain = @"com.hicknhack.macpass.error"; @implementation NSError (Messages) - (NSString *)descriptionForErrorCode { return [NSString stringWithFormat:@"%@ (%ld)", [self localizedDescription], [self code] ]; } + ++ (NSError *)errorWithCode:(NSInteger)code description:(NSString *)description { + return [[NSError alloc] initWithDomain:MPErrorDomain code:code userInfo:@{ NSLocalizedDescriptionKey: description }]; +} @end