diff --git a/KeePassKit b/KeePassKit index a030f320..4a383312 160000 --- a/KeePassKit +++ b/KeePassKit @@ -1 +1 @@ -Subproject commit a030f3209e47040ed71a821f7e0a57d48d364161 +Subproject commit 4a3833128e2290b0cbb5f3a6695039bdbae9f39a diff --git a/MacPass.xcodeproj/project.pbxproj b/MacPass.xcodeproj/project.pbxproj index 2a67ccb2..e5779f38 100644 --- a/MacPass.xcodeproj/project.pbxproj +++ b/MacPass.xcodeproj/project.pbxproj @@ -207,6 +207,7 @@ 4CD884B715BD47080042BBF8 /* DocumentWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4CD884B615BD47080042BBF8 /* DocumentWindow.xib */; }; 4CDB5C421794AA4F0017667E /* KPKTree+Serializing.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CDB5C411794AA4F0017667E /* KPKTree+Serializing.m */; }; 4CDF01A316D1B76700D0AC08 /* MPEntryViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CDF01A216D1B76700D0AC08 /* MPEntryViewController.m */; }; + 4CE298EB1795FC2A00DF7BDB /* MPEntryMenuDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CE298EA1795FC2A00DF7BDB /* MPEntryMenuDelegate.m */; }; 4CE39ABF16ECE34A000FE29D /* MPIconSelectViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CE39ABE16ECE34A000FE29D /* MPIconSelectViewController.m */; }; 4CE39AC116ECE359000FE29D /* IconSelection.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4CE39AC016ECE359000FE29D /* IconSelection.xib */; }; 4CE39AC416ECE4F7000FE29D /* MPPopupImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CE39AC316ECE4F7000FE29D /* MPPopupImageView.m */; }; @@ -603,6 +604,8 @@ 4CDB5C411794AA4F0017667E /* KPKTree+Serializing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "KPKTree+Serializing.m"; path = "../Core/KPKTree+Serializing.m"; sourceTree = ""; }; 4CDF01A116D1B76700D0AC08 /* MPEntryViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPEntryViewController.h; sourceTree = ""; }; 4CDF01A216D1B76700D0AC08 /* MPEntryViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPEntryViewController.m; sourceTree = ""; }; + 4CE298E91795FC2A00DF7BDB /* MPEntryMenuDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPEntryMenuDelegate.h; sourceTree = ""; }; + 4CE298EA1795FC2A00DF7BDB /* MPEntryMenuDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPEntryMenuDelegate.m; sourceTree = ""; }; 4CE39ABD16ECE34A000FE29D /* MPIconSelectViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPIconSelectViewController.h; sourceTree = ""; }; 4CE39ABE16ECE34A000FE29D /* MPIconSelectViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPIconSelectViewController.m; sourceTree = ""; }; 4CE39AC016ECE359000FE29D /* IconSelection.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = IconSelection.xib; sourceTree = ""; }; @@ -950,6 +953,8 @@ 4C3BD51416D276F800389F1F /* MPToolbarDelegate.m */, 4C811C8116ECD06E00C4BAC6 /* MPKeyfilePathControlDelegate.h */, 4C811C8216ECD06E00C4BAC6 /* MPKeyfilePathControlDelegate.m */, + 4CE298E91795FC2A00DF7BDB /* MPEntryMenuDelegate.h */, + 4CE298EA1795FC2A00DF7BDB /* MPEntryMenuDelegate.m */, ); name = Delegates; sourceTree = ""; @@ -1784,6 +1789,7 @@ 4CF62B86179385D700B660B6 /* KPKAttribute.m in Sources */, 4CDB5C421794AA4F0017667E /* KPKTree+Serializing.m in Sources */, 4C5AA591179549A1008ECAD7 /* KPKXmlTreeWriter.m in Sources */, + 4CE298EB1795FC2A00DF7BDB /* MPEntryMenuDelegate.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/MacPass/Base.lproj/InspectorView.xib b/MacPass/Base.lproj/InspectorView.xib index 66d02b5e..36c0ad64 100644 --- a/MacPass/Base.lproj/InspectorView.xib +++ b/MacPass/Base.lproj/InspectorView.xib @@ -136,7 +136,7 @@ - 268 + -2147483380 {{184, 5}, {45, 19}} @@ -165,7 +165,7 @@ - 268 + -2147483380 {{237, 5}, {36, 19}} diff --git a/MacPass/MPEntryMenuDelegate.h b/MacPass/MPEntryMenuDelegate.h new file mode 100644 index 00000000..322a3fd8 --- /dev/null +++ b/MacPass/MPEntryMenuDelegate.h @@ -0,0 +1,16 @@ +// +// MPEntryMenuDelegate.h +// MacPass +// +// Created by Michael Starke on 17.07.13. +// Copyright (c) 2013 HicknHack Software GmbH. All rights reserved. +// + +#import +@class MPEntryViewController; + +@interface MPEntryMenuDelegate : NSObject + +@property (weak) MPEntryViewController *viewController; + +@end diff --git a/MacPass/MPEntryMenuDelegate.m b/MacPass/MPEntryMenuDelegate.m new file mode 100644 index 00000000..555bf0bb --- /dev/null +++ b/MacPass/MPEntryMenuDelegate.m @@ -0,0 +1,54 @@ +// +// MPEntryMenuDelegate.m +// MacPass +// +// Created by Michael Starke on 17.07.13. +// Copyright (c) 2013 HicknHack Software GmbH. All rights reserved. +// + +#import "MPEntryMenuDelegate.h" +#import "MPEntryViewController.h" + +#import "Kdb4Node.h" + +static NSUInteger const kMPCustomFieldMenuItem = 1000; +static NSUInteger const kMPAttachmentsMenuItem = 2000; + +@implementation MPEntryMenuDelegate + +- (void)menuNeedsUpdate:(NSMenu *)menu { + NSMenuItem *fieldsMenu = [menu itemWithTag:kMPCustomFieldMenuItem]; + NSMenuItem *attachmentsMenu = [menu itemWithTag:kMPAttachmentsMenuItem]; + if(fieldsMenu) { + [menu removeItem:fieldsMenu]; + } + if(attachmentsMenu) { + [menu removeItem:attachmentsMenu]; + } + + NSMenuItem *lastItem = [[menu itemArray] lastObject]; + if([lastItem isSeparatorItem]) { + [menu removeItem:lastItem]; + } + + if([self.viewController.selectedEntry isKindOfClass:[Kdb4Entry class]]) { + Kdb4Entry *entry = (Kdb4Entry *)self.viewController.selectedEntry; + if([entry.stringFields count] > 0) { + [menu addItem:[NSMenuItem separatorItem]]; + NSMenuItem *customFieldsItem = [[NSMenuItem alloc] init]; + NSMenu *submenu = [[NSMenu alloc] initWithTitle:@"Fields"]; + [customFieldsItem setTitle:NSLocalizedString(@"COPY_CUSTOM_FIELDS", "Submenu to Copy custom fields")]; + [customFieldsItem setTag:kMPCustomFieldMenuItem]; + for (StringField *field in entry.stringFields) { + NSString *title = [NSString stringWithFormat:NSLocalizedString(@"COPY_FIELD_%@", "Mask for title to copy field value"), field.key]; + NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:title action:@selector(copyCustomField:) keyEquivalent:@""]; + [item setTag:[entry.stringFields indexOfObject:field]]; + [submenu addItem:item]; + } + [customFieldsItem setSubmenu:submenu]; + [menu addItem:customFieldsItem]; + } + } +} + +@end diff --git a/MacPass/MPEntryViewController.h b/MacPass/MPEntryViewController.h index 50230d25..cf389183 100644 --- a/MacPass/MPEntryViewController.h +++ b/MacPass/MPEntryViewController.h @@ -48,9 +48,11 @@ typedef NS_ENUM( NSUInteger, MPCopyContentTypeTag) { /* Copy/Paste */ - (void)copyUsername:(id)sender; - (void)copyPassword:(id)sender; +- (void)copyCustomField:(id)sender; - (void)copyURL:(id)sender; - (void)openURL:(id)sender; + /* Entry Handling*/ - (void)deleteNode:(id)sender; diff --git a/MacPass/MPEntryViewController.m b/MacPass/MPEntryViewController.m index cacbf81a..67594129 100644 --- a/MacPass/MPEntryViewController.m +++ b/MacPass/MPEntryViewController.m @@ -20,10 +20,12 @@ #import "MPConstants.h" #import "MPEntryTableDataSource.h" #import "MPStringLengthValueTransformer.h" +#import "MPEntryMenuDelegate.h" #import "HNHTableHeaderCell.h" #import "HNHGradientView.h" +#import "Kdb4Node.h" #import "KdbGroup+MPTreeTools.h" #import "KdbGroup+Undo.h" #import "KdbEntry+Undo.h" @@ -43,6 +45,7 @@ typedef NS_ENUM(NSUInteger,MPOVerlayInfoType) { MPOverlayInfoPassword, MPOverlayInfoUsername, MPOverlayInfoURL, + MPOverlayInfoCustom }; NSString *const MPEntryTableUserNameColumnIdentifier = @"MPUserNameColumnIdentifier"; @@ -59,7 +62,9 @@ NSString *const _toggleFilterURLButton = @"SearchURL"; NSString *const _toggleFilterTitleButton = @"SearchTitle"; NSString *const _toggleFilterUsernameButton = @"SearchUsername"; -@interface MPEntryViewController () +@interface MPEntryViewController () { + MPEntryMenuDelegate *_menuDelegate; +} @property (strong) NSArrayController *entryArrayController; @property (strong) NSArray *filteredEntries; @@ -103,6 +108,9 @@ NSString *const _toggleFilterUsernameButton = @"SearchUsername"; _entryArrayController = [[NSArrayController alloc] init]; _dataSource = [[MPEntryTableDataSource alloc] init]; _dataSource.viewController = self; + _menuDelegate = [[MPEntryMenuDelegate alloc] init]; + _menuDelegate.viewController = self; + _selectedEntry = nil; } return self; @@ -396,7 +404,7 @@ NSString *const _toggleFilterUsernameButton = @"SearchUsername"; } } -- (void)_copyToPasteboard:(NSString *)data overlayInfo:(MPOVerlayInfoType)overlayInfoType { +- (void)_copyToPasteboard:(NSString *)data overlayInfo:(MPOVerlayInfoType)overlayInfoType name:(NSString *)name{ if(data) { [[MPPasteBoardController defaultController] copyObjects:@[ data ]]; } @@ -417,6 +425,11 @@ NSString *const _toggleFilterUsernameButton = @"SearchUsername"; infoImage = [[NSBundle mainBundle] imageForResource:@"09_IdentityTemplate"]; infoText = NSLocalizedString(@"COPIED_USERNAME", @"Username was copied to the pasteboard"); break; + + case MPOverlayInfoCustom: + infoImage = [[NSBundle mainBundle] imageForResource:@"00_PasswordTemplate"]; + infoText = [NSString stringWithFormat:NSLocalizedString(@"COPIED_FIELD_%@", "Field nam that was copied to the pasteboard"), name]; + break; } [[MPOverlayWindowController sharedController] displayOverlayImage:infoImage label:infoText atView:self.view]; } @@ -430,7 +443,9 @@ NSString *const _toggleFilterUsernameButton = @"SearchUsername"; for(NSMenuItem *item in items) { [menu addItem:item]; } + [menu setDelegate:_menuDelegate]; [self.entryTable setMenu:menu]; + } #pragma makr Action Helper @@ -452,21 +467,32 @@ NSString *const _toggleFilterUsernameButton = @"SearchUsername"; - (void)copyPassword:(id)sender { KdbEntry *selectedEntry = [self _clickedOrSelectedEntry]; if(selectedEntry) { - [self _copyToPasteboard:selectedEntry.password overlayInfo:MPOverlayInfoPassword]; + [self _copyToPasteboard:selectedEntry.password overlayInfo:MPOverlayInfoPassword name:nil]; } } - (void)copyUsername:(id)sender { KdbEntry *selectedEntry = [self _clickedOrSelectedEntry]; if(selectedEntry) { - [self _copyToPasteboard:selectedEntry.username overlayInfo:MPOverlayInfoUsername]; + [self _copyToPasteboard:selectedEntry.username overlayInfo:MPOverlayInfoUsername name:nil]; + } +} + +- (void)copyCustomField:(id)sender { + KdbEntry *selectedEntry = [self _clickedOrSelectedEntry]; + if(selectedEntry && [selectedEntry isKindOfClass:[Kdb4Entry class]]) { + Kdb4Entry *entry = (Kdb4Entry *)selectedEntry; + NSUInteger index = [sender tag]; + NSAssert((index >= 0) && (index < [entry.stringFields count]), @"Index for custom field needs to be valid"); + StringField *field = entry.stringFields[index]; + [self _copyToPasteboard:field.value overlayInfo:MPOverlayInfoCustom name:field.key]; } } - (void)copyURL:(id)sender { KdbEntry *selectedEntry = [self _clickedOrSelectedEntry]; if(selectedEntry) { - [self _copyToPasteboard:selectedEntry.url overlayInfo:MPOverlayInfoURL]; + [self _copyToPasteboard:selectedEntry.url overlayInfo:MPOverlayInfoURL name:nil]; } } diff --git a/MacPass/de.lproj/Localizable.strings b/MacPass/de.lproj/Localizable.strings index 5da3bc8d..d25a0661 100644 Binary files a/MacPass/de.lproj/Localizable.strings and b/MacPass/de.lproj/Localizable.strings differ diff --git a/MacPass/en.lproj/Localizable.strings b/MacPass/en.lproj/Localizable.strings index 805634a5..6f270913 100644 Binary files a/MacPass/en.lproj/Localizable.strings and b/MacPass/en.lproj/Localizable.strings differ