diff --git a/MacPass.xcodeproj/project.pbxproj b/MacPass.xcodeproj/project.pbxproj index 99906bce..f1ae457f 100644 --- a/MacPass.xcodeproj/project.pbxproj +++ b/MacPass.xcodeproj/project.pbxproj @@ -244,6 +244,7 @@ 4CD3ABBF178F72610073F5C5 /* KPKEntry.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CD3ABBE178F72610073F5C5 /* KPKEntry.m */; }; 4CD3ABC2178F72720073F5C5 /* KPKGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CD3ABC1178F72720073F5C5 /* KPKGroup.m */; }; 4CD4901018513C180017397F /* KPKXmlElements.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CD4900F18513C180017397F /* KPKXmlElements.m */; }; + 4CD5A79118BFE17900162DCB /* CloneEntryWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4CD5A79018BFE17900162DCB /* CloneEntryWindow.xib */; }; 4CD5D702177A5EE400100649 /* DatabaseSettingsWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4CD5D701177A5EE400100649 /* DatabaseSettingsWindow.xib */; }; 4CD5D705177A5F3300100649 /* MPDatabaseSettingsWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CD5D704177A5F3300100649 /* MPDatabaseSettingsWindowController.m */; }; 4CD6C5AE1789FDE6000891F6 /* HNHRoundedSecureTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CD6C5AD1789FDE6000891F6 /* HNHRoundedSecureTextField.m */; }; @@ -779,6 +780,7 @@ 4CD3ABC1178F72720073F5C5 /* KPKGroup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KPKGroup.m; sourceTree = ""; }; 4CD4900E18513C180017397F /* KPKXmlElements.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = KPKXmlElements.h; path = Format/KPKXmlElements.h; sourceTree = ""; }; 4CD4900F18513C180017397F /* KPKXmlElements.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = KPKXmlElements.m; path = Format/KPKXmlElements.m; sourceTree = ""; }; + 4CD5A79018BFE17900162DCB /* CloneEntryWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CloneEntryWindow.xib; sourceTree = ""; }; 4CD5D701177A5EE400100649 /* DatabaseSettingsWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = DatabaseSettingsWindow.xib; sourceTree = ""; }; 4CD5D703177A5F3300100649 /* MPDatabaseSettingsWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPDatabaseSettingsWindowController.h; sourceTree = ""; }; 4CD5D704177A5F3300100649 /* MPDatabaseSettingsWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPDatabaseSettingsWindowController.m; sourceTree = ""; }; @@ -1787,6 +1789,7 @@ 4C0728B917B5B7A4005A7DD9 /* PasswordEditWindow.xib */, 4C68456E17BC2A0700FCDBFC /* WelcomeWindow.xib */, 4C0DD6C518B2A44700FCB193 /* AutotypeCandidateSelectionWindow.xib */, + 4CD5A79018BFE17900162DCB /* CloneEntryWindow.xib */, ); name = Windows; sourceTree = ""; @@ -2006,6 +2009,7 @@ 4C5A11FF1708DE8800223D8A /* PasswordCreatorView.xib in Resources */, 4C1DDCDD1711ECEB00C98DA3 /* PasswordCreatorWindow.xib in Resources */, 4C7714AA176C998F00549F2A /* 43_TrashTemplate.pdf in Resources */, + 4CD5A79118BFE17900162DCB /* CloneEntryWindow.xib in Resources */, 4C7714AC176C9D4600549F2A /* 99_InfoTemplate.pdf in Resources */, 4CF7805F176E75110032EE71 /* IntegrationSettings.xib in Resources */, 4CA3531218A5577300839B0F /* dsa_sparkle_pub.pem in Resources */, diff --git a/MacPass/CloneEntryWindow.xib b/MacPass/CloneEntryWindow.xib new file mode 100644 index 00000000..b8f7192d --- /dev/null +++ b/MacPass/CloneEntryWindow.xib @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MacPass/MPActionHelper.m b/MacPass/MPActionHelper.m index cb076c2b..6d81f121 100644 --- a/MacPass/MPActionHelper.m +++ b/MacPass/MPActionHelper.m @@ -15,22 +15,23 @@ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ actionDict = @{ - @(MPActionAddEntry): @"createEntry:", - @(MPActionAddGroup): @"createGroup:", - @(MPActionCloneEntry): @"cloneEntry:", - @(MPActionCopyPassword): @"copyPassword:", - @(MPActionCopyURL): @"copyURL:", - @(MPActionCopyUsername): @"copyUsername:", - @(MPActionDelete): @"delete:", - @(MPActionEditPassword): @"editPassword:", - @(MPActionOpenURL): @"openURL:", - @(MPActionToggleInspector): @"toggleInspector:", - @(MPActionLock): @"lock:", - @(MPActionEmptyTrash): @"emptyTrash:", - @(MPActionDatabaseSettings): @"showDatabaseSettings:", - @(MPActionEditTemplateGroup): @"editTemplateGroup:", - @(MPActionExportXML): @"exportAsXML", - @(MPActionImportXML): @"importFromXMl", + @(MPActionAddEntry): @"createEntry:", + @(MPActionAddGroup): @"createGroup:", + @(MPActionCloneEntry): @"cloneEntry:", + @(MPActionCloneEntryWithOptions): @"cloneEntryWithOptions:", + @(MPActionCopyPassword): @"copyPassword:", + @(MPActionCopyURL): @"copyURL:", + @(MPActionCopyUsername): @"copyUsername:", + @(MPActionDelete): @"delete:", + @(MPActionEditPassword): @"editPassword:", + @(MPActionOpenURL): @"openURL:", + @(MPActionToggleInspector): @"toggleInspector:", + @(MPActionLock): @"lock:", + @(MPActionEmptyTrash): @"emptyTrash:", + @(MPActionDatabaseSettings): @"showDatabaseSettings:", + @(MPActionEditTemplateGroup): @"editTemplateGroup:", + @(MPActionExportXML): @"exportAsXML", + @(MPActionImportXML): @"importFromXMl", }; }); return actionDict; diff --git a/MacPass/MPContextMenuHelper.h b/MacPass/MPContextMenuHelper.h index 96e6ad80..6c94a152 100644 --- a/MacPass/MPContextMenuHelper.h +++ b/MacPass/MPContextMenuHelper.h @@ -13,8 +13,9 @@ typedef NS_OPTIONS(NSUInteger, MPContextMenuItemsFlags) { MPContextMenuDelete = 1 << 1, MPContextMenuCopy = 1 << 2, MPContextMenuTrash = 1 << 3, + MPContextMenuClone = 1 << 4, MPContextMenuMinimal = MPContextMenuCreate | MPContextMenuDelete, - MPContextMenuFull = MPContextMenuMinimal | MPContextMenuCopy, + MPContextMenuFull = MPContextMenuMinimal | MPContextMenuCopy | MPContextMenuClone, MPContextMenuExtended = MPContextMenuFull | MPContextMenuTrash }; diff --git a/MacPass/MPContextMenuHelper.m b/MacPass/MPContextMenuHelper.m index 4973cf75..d1d1eaa0 100644 --- a/MacPass/MPContextMenuHelper.m +++ b/MacPass/MPContextMenuHelper.m @@ -25,6 +25,7 @@ static void MPContextmenuHelperBeginSection(NSMutableArray *items) { BOOL const insertDelete = MPTestFlagInOptions(MPContextMenuDelete, flags); BOOL const insertCopy = MPTestFlagInOptions(MPContextMenuCopy, flags); BOOL const insertTrash = MPTestFlagInOptions(MPContextMenuTrash, flags); + BOOL const insertClone = MPTestFlagInOptions(MPContextMenuClone, flags); NSMutableArray *items = [NSMutableArray arrayWithCapacity:10]; if(insertCreate) { @@ -38,6 +39,18 @@ static void MPContextmenuHelperBeginSection(NSMutableArray *items) { [items addObjectsFromArray:@[ newGroup, newEntry ]]; } + if(insertClone) { + MPContextmenuHelperBeginSection(items); + NSMenuItem *cloneEntry = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"CLONE_ENTRY", @"") + action:[MPActionHelper actionOfType:MPActionCloneEntry] + keyEquivalent:@"D"]; + NSMenuItem *cloneEntyWithOptions = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"CLONE_ENTRY_WITH_OPTIONS", @"") + action:[MPActionHelper actionOfType:MPActionCloneEntryWithOptions] + keyEquivalent:@""]; + + [items addObjectsFromArray:@[ cloneEntry, cloneEntyWithOptions ]]; + + } if(insertDelete || insertTrash) { MPContextmenuHelperBeginSection(items); if(insertDelete) { diff --git a/MacPass/MPDocument.h b/MacPass/MPDocument.h index 138f1341..4077fd7d 100644 --- a/MacPass/MPDocument.h +++ b/MacPass/MPDocument.h @@ -165,4 +165,8 @@ typedef NS_OPTIONS(NSUInteger, MPEntrySearchFlags) { */ - (IBAction)createEntryFromTemplate:(id)sender; +- (IBAction)cloneEntry:(id)sender; + +- (IBAction)cloneEntryWithOptions:(id)sender; + @end \ No newline at end of file diff --git a/MacPass/MPDocument.m b/MacPass/MPDocument.m index 5d7b1d85..ab57b8f8 100644 --- a/MacPass/MPDocument.m +++ b/MacPass/MPDocument.m @@ -489,6 +489,7 @@ NSString *const MPDocumentGroupKey = @"MPDocumentGroupKey #pragma mark Actions + - (void)emptyTrash:(id)sender { NSAlert *alert = [[NSAlert alloc] init]; [alert setAlertStyle:NSWarningAlertStyle]; @@ -528,6 +529,18 @@ NSString *const MPDocumentGroupKey = @"MPDocumentGroupKey } } +- (void)cloneEntry:(id)sender { + KPKEntry *clone = [self.selectedEntry copyWithTitle:nil]; + NSInteger index = [self.selectedEntry.parent.entries indexOfObject:self.selectedEntry]; + [self.selectedEntry.parent addEntry:clone atIndex:index+1]; + [self.undoManager setActionName:NSLocalizedString(@"CLONE_ENTRY", "")]; +} + +- (void)cloneEntryWithOptions:(id)sender { +} + + +#pragma mark Validation - (BOOL)validateMenuItem:(NSMenuItem *)menuItem { return [self validateUserInterfaceItem:menuItem]; } @@ -538,7 +551,7 @@ NSString *const MPDocumentGroupKey = @"MPDocumentGroupKey - (BOOL)validateUserInterfaceItem:(id)anItem { if(self.encrypted || self.isReadOnly) { return NO; } - + BOOL valid = YES; switch([MPActionHelper typeForAction:[anItem action]]) { case MPActionAddGroup: @@ -551,6 +564,11 @@ NSString *const MPDocumentGroupKey = @"MPDocumentGroupKey valid &= (self.trash != self.selectedItem); valid &= ![self isItemTrashed:self.selectedItem]; break; + case MPActionCloneEntry: + //case MPActionCloneEntryWithOptions: + valid &= (nil != self.selectedItem); + valid &= self.selectedEntry == self.selectedItem; + break; case MPActionEmptyTrash: valid &= [self.trash.groups count] > 0; valid &= [self.trash.entries count] > 0; diff --git a/MacPass/en.lproj/Localizable.strings b/MacPass/en.lproj/Localizable.strings index 04334531..fe8dcd1e 100644 Binary files a/MacPass/en.lproj/Localizable.strings and b/MacPass/en.lproj/Localizable.strings differ