diff --git a/MacPass.xcodeproj/project.pbxproj b/MacPass.xcodeproj/project.pbxproj index bc453a57..cf6a437f 100644 --- a/MacPass.xcodeproj/project.pbxproj +++ b/MacPass.xcodeproj/project.pbxproj @@ -103,6 +103,7 @@ 4C46B88517063A070046109A /* NSString+MPPasswordCreation.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C46B88417063A070046109A /* NSString+MPPasswordCreation.m */; }; 4C473A8718AFD85B0073FD2E /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C473A8518AFD7250073FD2E /* XCTest.framework */; }; 4C4A100F176286FD00BBF2CA /* MPTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C4A100E176286FD00BBF2CA /* MPTableView.m */; }; + 4C4B5B3A1D759A4E005F329A /* MPDocument+ModelChange.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C4B5B391D759A4E005F329A /* MPDocument+ModelChange.m */; }; 4C4B728518E4B9B400A1A5D5 /* MPDockTileHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C4B728418E4B9B400A1A5D5 /* MPDockTileHelper.m */; }; 4C4B7EE917A45EC6000234C7 /* MPDatePickingViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C4B7EE717A45EC5000234C7 /* MPDatePickingViewController.m */; }; 4C4B7EEE17A467E1000234C7 /* MPGroupInspectorViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C4B7EEC17A467E1000234C7 /* MPGroupInspectorViewController.m */; }; @@ -140,7 +141,6 @@ 4C6F228C19A4AA700012310C /* MPAutotypeDelay.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C6F228B19A4AA700012310C /* MPAutotypeDelay.m */; }; 4C701CBC178618A000581B88 /* 12_RemoteTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 4C701CBB178618A000581B88 /* 12_RemoteTemplate.pdf */; }; 4C7155D81A10DB6D00979307 /* IconSelection.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4C7155DA1A10DB6D00979307 /* IconSelection.xib */; }; - 4C74DD07177BD1640034A9DB /* MPCustomFieldView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C74DD06177BD1640034A9DB /* MPCustomFieldView.m */; }; 4C76155C1764C04C0015A1A6 /* GeneralSettings.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4C76155E1764C04C0015A1A6 /* GeneralSettings.xib */; }; 4C7615681764C0C40015A1A6 /* PasswordInputView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4C76156A1764C0C40015A1A6 /* PasswordInputView.xib */; }; 4C76156D1764C0E20015A1A6 /* InspectorView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4C76156F1764C0E20015A1A6 /* InspectorView.xib */; }; @@ -195,8 +195,6 @@ 4CAD748E15B88AC100104512 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CAD748D15B88AC100104512 /* libz.dylib */; }; 4CB915941A0159A20089CE5B /* DuplicateEntryOptionsWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4CB915931A0159A20089CE5B /* DuplicateEntryOptionsWindow.xib */; }; 4CB9339916D3A0DD00A13B5D /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 4CB9339716D3A0DD00A13B5D /* Credits.rtf */; }; - 4CB9D77F1D70CE6B00F43F76 /* MPModelChangeObserving.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CB9D77E1D70CE6B00F43F76 /* MPModelChangeObserving.m */; }; - 4CB9D7821D71AFBE00F43F76 /* MPArrayController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CB9D7811D71AFBE00F43F76 /* MPArrayController.m */; }; 4CBA2ABA17074C07006D8139 /* MPSettingsHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CBA2AB917074C07006D8139 /* MPSettingsHelper.m */; }; 4CC0D2CE17974A47000B4BDA /* MPCustomFieldTableViewDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CC0D2CD17974A47000B4BDA /* MPCustomFieldTableViewDelegate.m */; }; 4CC0D2D117974A5A000B4BDA /* MPAttachmentTableViewDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CC0D2D017974A5A000B4BDA /* MPAttachmentTableViewDelegate.m */; }; @@ -222,7 +220,6 @@ 4CD78AC016D155FF00768A1D /* 11_CameraTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 4CD78ABB16D155FF00768A1D /* 11_CameraTemplate.pdf */; }; 4CD820211A32173100399DBB /* ReferenceBuilderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4CD820231A32173100399DBB /* ReferenceBuilderView.xib */; }; 4CD884B715BD47080042BBF8 /* DocumentWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4CD884B615BD47080042BBF8 /* DocumentWindow.xib */; }; - 4CDE7B161D660983008C4160 /* MPObjectController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CDE7B151D660983008C4160 /* MPObjectController.m */; }; 4CDF01A316D1B76700D0AC08 /* MPEntryViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CDF01A216D1B76700D0AC08 /* MPEntryViewController.m */; }; 4CE2961518429AA5005F01CE /* MPAutotypeKeyPress.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CE2961418429AA5005F01CE /* MPAutotypeKeyPress.m */; }; 4CE296191842A166005F01CE /* MPAutotypePaste.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CE296181842A166005F01CE /* MPAutotypePaste.m */; }; @@ -233,7 +230,6 @@ 4CE3E62617AB0D2D00D9E4B4 /* MPAttachmentTableDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CE3E62517AB0D2D00D9E4B4 /* MPAttachmentTableDataSource.m */; }; 4CE501341BBC47F500FB819D /* MPTagsTokenFieldDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CE501331BBC47F500FB819D /* MPTagsTokenFieldDelegate.m */; }; 4CE5B54B173AFBA700207B39 /* MPDocument.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CE5B549173AFBA700207B39 /* MPDocument.m */; }; - 4CE7FAFB1D74813500E2C856 /* MPTestModelChangeObservingHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CE7FAFA1D74813500E2C856 /* MPTestModelChangeObservingHelper.m */; }; 4CE8246F16E2E93400573141 /* MPOverlayWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CE8246E16E2E93400573141 /* MPOverlayWindowController.m */; }; 4CE8247516E2F2B900573141 /* MPOverlayView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CE8247416E2F2B900573141 /* MPOverlayView.m */; }; 4CE88B9717BA651C0042E078 /* contextTriangleTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 4CE88B9617BA651C0042E078 /* contextTriangleTemplate.pdf */; }; @@ -418,6 +414,7 @@ 4C473A8518AFD7250073FD2E /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 4C4A100D176286FD00BBF2CA /* MPTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPTableView.h; sourceTree = ""; }; 4C4A100E176286FD00BBF2CA /* MPTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPTableView.m; sourceTree = ""; }; + 4C4B5B391D759A4E005F329A /* MPDocument+ModelChange.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MPDocument+ModelChange.m"; sourceTree = ""; }; 4C4B728318E4B9B400A1A5D5 /* MPDockTileHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPDockTileHelper.h; sourceTree = ""; }; 4C4B728418E4B9B400A1A5D5 /* MPDockTileHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPDockTileHelper.m; sourceTree = ""; }; 4C4B7EE617A45EC5000234C7 /* MPDatePickingViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPDatePickingViewController.h; sourceTree = ""; }; @@ -493,8 +490,6 @@ 4C7155EA1A10DB7800979307 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/IconSelection.strings; sourceTree = ""; }; 4C7155EC1A10DB7900979307 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/IconSelection.strings; sourceTree = ""; }; 4C725F3F19EEF4FC00191B01 /* MJGAvailability.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MJGAvailability.h; sourceTree = ""; }; - 4C74DD05177BD1640034A9DB /* MPCustomFieldView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPCustomFieldView.h; sourceTree = ""; }; - 4C74DD06177BD1640034A9DB /* MPCustomFieldView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPCustomFieldView.m; sourceTree = ""; }; 4C76155F1764C0590015A1A6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/GeneralSettings.xib; sourceTree = ""; }; 4C7615601764C05A0015A1A6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; 4C76156B1764C0C80015A1A6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/PasswordInputView.xib; sourceTree = ""; }; @@ -592,10 +587,6 @@ 4CB63A6018986530002DEC4C /* MPFlagsHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MPFlagsHelper.h; sourceTree = ""; }; 4CB915931A0159A20089CE5B /* DuplicateEntryOptionsWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = DuplicateEntryOptionsWindow.xib; sourceTree = ""; }; 4CB9339816D3A0DD00A13B5D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = en; path = en.lproj/Credits.rtf; sourceTree = ""; }; - 4CB9D77C1D70CBCE00F43F76 /* MPModelChangeObserving.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPModelChangeObserving.h; sourceTree = ""; }; - 4CB9D77E1D70CE6B00F43F76 /* MPModelChangeObserving.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPModelChangeObserving.m; sourceTree = ""; }; - 4CB9D7801D71AFBE00F43F76 /* MPArrayController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPArrayController.h; sourceTree = ""; }; - 4CB9D7811D71AFBE00F43F76 /* MPArrayController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPArrayController.m; sourceTree = ""; }; 4CBA2AB617074B59006D8139 /* MPSettingsHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSettingsHelper.h; sourceTree = ""; }; 4CBA2AB917074C07006D8139 /* MPSettingsHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSettingsHelper.m; sourceTree = ""; }; 4CC0D2CC17974A47000B4BDA /* MPCustomFieldTableViewDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPCustomFieldTableViewDelegate.h; sourceTree = ""; }; @@ -635,8 +626,6 @@ 4CD78ABB16D155FF00768A1D /* 11_CameraTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = 11_CameraTemplate.pdf; sourceTree = ""; }; 4CD820221A32173100399DBB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/ReferenceBuilderView.xib; sourceTree = ""; }; 4CD884B615BD47080042BBF8 /* DocumentWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = DocumentWindow.xib; sourceTree = ""; }; - 4CDE7B141D660983008C4160 /* MPObjectController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPObjectController.h; sourceTree = ""; }; - 4CDE7B151D660983008C4160 /* MPObjectController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPObjectController.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 = ""; }; 4CE2961318429AA5005F01CE /* MPAutotypeKeyPress.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAutotypeKeyPress.h; sourceTree = ""; }; @@ -657,7 +646,6 @@ 4CE501331BBC47F500FB819D /* MPTagsTokenFieldDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPTagsTokenFieldDelegate.m; sourceTree = ""; }; 4CE5B548173AFBA700207B39 /* MPDocument.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPDocument.h; sourceTree = ""; }; 4CE5B549173AFBA700207B39 /* MPDocument.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = MPDocument.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; - 4CE7FAFA1D74813500E2C856 /* MPTestModelChangeObservingHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPTestModelChangeObservingHelper.m; sourceTree = ""; }; 4CE8246D16E2E93400573141 /* MPOverlayWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPOverlayWindowController.h; sourceTree = ""; }; 4CE8246E16E2E93400573141 /* MPOverlayWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPOverlayWindowController.m; sourceTree = ""; }; 4CE8247316E2F2B900573141 /* MPOverlayView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPOverlayView.h; sourceTree = ""; }; @@ -836,8 +824,6 @@ 6021FE7918E15FF300C3BC51 /* DatePickingView.xib */, 4C26C34A18D8D5A300CF1A1C /* PreviewView.xib */, 4CD820231A32173100399DBB /* ReferenceBuilderView.xib */, - 4C74DD05177BD1640034A9DB /* MPCustomFieldView.h */, - 4C74DD06177BD1640034A9DB /* MPCustomFieldView.m */, 4CE8247316E2F2B900573141 /* MPOverlayView.h */, 4CE8247416E2F2B900573141 /* MPOverlayView.m */, 4CFC53BD16E94729007396BE /* MPShadowBox.h */, @@ -977,6 +963,7 @@ 4C1FA07A18231900003A3F8C /* MPDocument+Autotype.m */, 4C15B74518BCA3B1003F8008 /* MPDocument+Search.m */, 4C6B7C7C18BE7EB0001D5D77 /* MPDocument+HistoryBrowsing.m */, + 4C4B5B391D759A4E005F329A /* MPDocument+ModelChange.m */, 4C0AF62D195C1F2B009E658D /* MPEntrySearchContext.h */, 4C0AF62E195C1F2B009E658D /* MPEntrySearchContext.m */, 4CB44EEE1C972BFD00EE2D60 /* Proxies */, @@ -993,7 +980,6 @@ 4C45FB2C178E0BCB0010007D /* MPDatabaseLoading.m */, 4C45FB2F178E0CE20010007D /* MPTestDocument.m */, 4C6BC65F1A36717E00BDDF3D /* MPDatabaseSearch.m */, - 4CE7FAFA1D74813500E2C856 /* MPTestModelChangeObservingHelper.m */, 4C45FB1F178E09ED0010007D /* Supporting Files */, ); path = MacPassTests; @@ -1308,8 +1294,6 @@ children = ( 4CA0B30D15BCB6FD00654E32 /* MPSettingsTab.h */, 4C2B0B7419F66F6400E48913 /* MPTargetNodeResolving.h */, - 4CB9D77C1D70CBCE00F43F76 /* MPModelChangeObserving.h */, - 4CB9D77E1D70CE6B00F43F76 /* MPModelChangeObserving.m */, ); name = Protocolls; sourceTree = ""; @@ -1373,10 +1357,6 @@ 4C431BCC16E2A82700700A81 /* MPPasteBoardController.m */, 4C6AEEF71A043E2B00CA2420 /* MPDocumentController.h */, 4C6AEEF81A043E2B00CA2420 /* MPDocumentController.m */, - 4CDE7B141D660983008C4160 /* MPObjectController.h */, - 4CDE7B151D660983008C4160 /* MPObjectController.m */, - 4CB9D7801D71AFBE00F43F76 /* MPArrayController.h */, - 4CB9D7811D71AFBE00F43F76 /* MPArrayController.m */, ); name = "Data Controller"; sourceTree = ""; @@ -1682,7 +1662,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 4CE7FAFB1D74813500E2C856 /* MPTestModelChangeObservingHelper.m in Sources */, 4C45FB2D178E0BCB0010007D /* MPDatabaseLoading.m in Sources */, 4C8DEAA21C314D2C00D24C32 /* MPTestAutotypeDelay.m in Sources */, 4C45FB30178E0CE20010007D /* MPTestDocument.m in Sources */, @@ -1705,11 +1684,11 @@ 4CA0B2FC15BCAF8600654E32 /* MPSettingsWindowController.m in Sources */, 4C4F72D118DF704400E8D378 /* DDHotKeyTextField.m in Sources */, 4C83814215BF4677001AE468 /* MPDocumentWindowController.m in Sources */, + 4C4B5B3A1D759A4E005F329A /* MPDocument+ModelChange.m in Sources */, 4CD034AB1BFE113B003C002C /* MPPluginHost.m in Sources */, 4C2E382316D1421B00037A9D /* MPIconHelper.m in Sources */, 4C2E382616D1470200037A9D /* MPViewController.m in Sources */, 4C65FAE916D16DDB006E0577 /* MPPasswordInputController.m in Sources */, - 4CB9D7821D71AFBE00F43F76 /* MPArrayController.m in Sources */, 4CDF01A316D1B76700D0AC08 /* MPEntryViewController.m in Sources */, 4C3BD51516D276F800389F1F /* MPToolbarDelegate.m in Sources */, 4C7B63731C0CB51F00D7038C /* TTTDataTransformer.m in Sources */, @@ -1720,7 +1699,6 @@ 4CE8246F16E2E93400573141 /* MPOverlayWindowController.m in Sources */, 4CE8247516E2F2B900573141 /* MPOverlayView.m in Sources */, 4C7B63761C0CB51F00D7038C /* TTTJSONTransformer.m in Sources */, - 4CB9D77F1D70CE6B00F43F76 /* MPModelChangeObserving.m in Sources */, 4C77547516E55FE800970E02 /* MPInspectorViewController.m in Sources */, 4C89F524182FB4740069C73C /* MPAutotypeCommand.m in Sources */, 4CFC53BF16E94729007396BE /* MPShadowBox.m in Sources */, @@ -1740,7 +1718,6 @@ 4CE296191842A166005F01CE /* MPAutotypePaste.m in Sources */, 4C569D9E17652B0600595B62 /* MPConstants.m in Sources */, 4C569DA117652BFE00595B62 /* MPEntryTableDataSource.m in Sources */, - 4CDE7B161D660983008C4160 /* MPObjectController.m in Sources */, 4C7B63711C0CB51F00D7038C /* NSValueTransformer+TransformerKit.m in Sources */, 4CD034AA1BFE113B003C002C /* MPPlugin.m in Sources */, 4CA2335A176DBFE100F0B6AC /* MPLockDaemon.m in Sources */, @@ -1761,7 +1738,6 @@ 4C25703F1BF11C2300D39416 /* MPPluginSettingsController.m in Sources */, 4CF6C711176F4533007A811D /* MPStringLengthValueTransformer.m in Sources */, 4CD5D705177A5F3300100649 /* MPDatabaseSettingsWindowController.m in Sources */, - 4C74DD07177BD1640034A9DB /* MPCustomFieldView.m in Sources */, 4C4FCE15177CFE6B00BBF7AE /* MPCustomFieldTableCellView.m in Sources */, 4C26C34B18D8D5A300CF1A1C /* MPPreviewViewController.m in Sources */, 4C4B728518E4B9B400A1A5D5 /* MPDockTileHelper.m in Sources */, diff --git a/MacPass/MPCustomFieldTableCellView.m b/MacPass/MPCustomFieldTableCellView.m index bebd358d..3df1ee31 100644 --- a/MacPass/MPCustomFieldTableCellView.m +++ b/MacPass/MPCustomFieldTableCellView.m @@ -7,16 +7,19 @@ // #import "MPCustomFieldTableCellView.h" +#import "MPDocument.h" @implementation MPCustomFieldTableCellView - - (void)setBackgroundStyle:(NSBackgroundStyle)backgroundStyle { - [super setBackgroundStyle:NSBackgroundStyleLight]; + super.backgroundStyle = NSBackgroundStyleLight; } - (void)setValue:(id)value forKeyPath:(NSString *)keyPath { - NSLog(@"%@ setValue:forKeyPath:%@", NSStringFromClass([self class]), keyPath); + if([keyPath hasPrefix:@"objectValue."]) { + [self.window.windowController.document willChangeModelProperty]; + [super setValue:value forKeyPath:keyPath]; + [self.window.windowController.document didChangeModelProperty]; + } [super setValue:value forKeyPath:keyPath]; } - @end diff --git a/MacPass/MPCustomFieldTableViewDelegate.m b/MacPass/MPCustomFieldTableViewDelegate.m index a3e62ecf..3293effb 100644 --- a/MacPass/MPCustomFieldTableViewDelegate.m +++ b/MacPass/MPCustomFieldTableViewDelegate.m @@ -30,7 +30,7 @@ - (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row { MPCustomFieldTableCellView *view = [tableView makeViewWithIdentifier:@"SelectedCell" owner:tableView]; - + [view.labelTextField bind:NSValueBinding toObject:view withKeyPath:[NSString stringWithFormat:@"%@.%@", NSStringFromSelector(@selector(objectValue)), NSStringFromSelector(@selector(key))] diff --git a/MacPass/MPDatePickingViewController.m b/MacPass/MPDatePickingViewController.m index e8621ba9..b80ea71c 100644 --- a/MacPass/MPDatePickingViewController.m +++ b/MacPass/MPDatePickingViewController.m @@ -60,9 +60,9 @@ typedef NS_ENUM(NSUInteger, MPDatePreset) { - (IBAction)useDate:(id)sender { KPKTimeInfo *timeInfo = [self.representedObject timeInfo]; - [self willChangeValueForRepresentedObjectKeyPath:[NSString stringWithFormat:@"%@.%@.%@", NSStringFromSelector(@selector(representedObject)), NSStringFromSelector(@selector(timeInfo)), NSStringFromSelector(@selector(expirationDate))]]; + [self.windowController.document willChangeModelProperty]; timeInfo.expirationDate = self.datePicker.dateValue; - [self didChangeValueForRepresentedObjectKeyPath:[NSString stringWithFormat:@"%@.%@.%@", NSStringFromSelector(@selector(representedObject)), NSStringFromSelector(@selector(timeInfo)), NSStringFromSelector(@selector(expirationDate))]]; + [self.windowController.document didChangeModelProperty]; [self.view.window performClose:sender]; } diff --git a/MacPass/MPDocument+ModelChange.m b/MacPass/MPDocument+ModelChange.m new file mode 100644 index 00000000..9c963271 --- /dev/null +++ b/MacPass/MPDocument+ModelChange.m @@ -0,0 +1,24 @@ +// +// MPDocument+ModelChange.m +// MacPass +// +// Created by Michael Starke on 30/08/16. +// Copyright © 2016 HicknHack Software GmbH. All rights reserved. +// + +#import "MPDocument.h" + +NSString *const MPDocumentWillChangeModelPropertyNotification = @"com.hicknhack.macpass.MPDocumentWillChangeModelPropertyNotification"; +NSString *const MPDocumentDidChangeModelPropertyNotification = @"com.hicknhack.macpass.MPDocumentDidChangeModelPropertyNotification"; + +@implementation MPDocument (ModelChange) + +- (void)willChangeModelProperty { + [[NSNotificationCenter defaultCenter] postNotificationName:MPDocumentWillChangeModelPropertyNotification object:self]; +} + +- (void)didChangeModelProperty { + [[NSNotificationCenter defaultCenter] postNotificationName:MPDocumentDidChangeModelPropertyNotification object:self]; +} + +@end diff --git a/MacPass/MPDocument.h b/MacPass/MPDocument.h index 6cc32388..539a2814 100644 --- a/MacPass/MPDocument.h +++ b/MacPass/MPDocument.h @@ -224,6 +224,27 @@ FOUNDATION_EXPORT NSString *const MPDocumentDidExitHistoryNotification; @end +#pragma mark - +#pragma mark ModelChanges +/** + * Notifications will be posted by the document to inform about changes to a model property being displayed + */ +FOUNDATION_EXPORT NSString *const MPDocumentWillChangeModelPropertyNotification; +FOUNDATION_EXPORT NSString *const MPDocumentDidChangeModelPropertyNotification; + +@interface MPDocument (ModelChange) + +/** + * Call this on the document ot inform it about changes about to happen. This is an entry point for views and controllers to notify others about changes done to the model. + * You coudl also observer modification times on objects but this way it's impossible to determine the source of the change. Undo/Redo or external plugins or merging can all introduce + * changes to a database that aren't relevant. + * The main use case for this is to update the history of entries on editing + */ +- (void)willChangeModelProperty; +- (void)didChangeModelProperty; + +@end + #pragma mark - #pragma mark Search diff --git a/MacPass/MPIconSelectViewController.m b/MacPass/MPIconSelectViewController.m index 9c16ba4f..c7631366 100644 --- a/MacPass/MPIconSelectViewController.m +++ b/MacPass/MPIconSelectViewController.m @@ -34,9 +34,9 @@ - (IBAction)useDefault:(id)sender { KPKNode *node = self.representedObject; - [self willChangeValueForRepresentedObjectKeyPath:[NSString stringWithFormat:@"%@.%@", NSStringFromSelector(@selector(representedObject)), NSStringFromSelector(@selector(iconId))]]; + [self.windowController.document willChangeModelProperty]; node.iconId = [[node class] defaultIcon]; - [self didChangeValueForRepresentedObjectKeyPath:[NSString stringWithFormat:@"%@.%@", NSStringFromSelector(@selector(representedObject)), NSStringFromSelector(@selector(iconId))]]; + [self.windowController.document didChangeModelProperty]; [self.view.window performClose:sender]; } @@ -50,9 +50,9 @@ NSUInteger buttonIndex = [self.iconCollectionView.content indexOfObject:image]; NSInteger newIconId = ((NSNumber *)[MPIconHelper databaseIconTypes][buttonIndex]).integerValue; KPKNode *node = self.representedObject; - [self willChangeValueForRepresentedObjectKeyPath:[NSString stringWithFormat:@"%@.%@", NSStringFromSelector(@selector(representedObject)), NSStringFromSelector(@selector(iconId))]]; + [self.windowController.document willChangeModelProperty]; node.iconId = newIconId; - [self didChangeValueForRepresentedObjectKeyPath:[NSString stringWithFormat:@"%@.%@", NSStringFromSelector(@selector(representedObject)), NSStringFromSelector(@selector(iconId))]]; + [self.windowController.document didChangeModelProperty]; [self.view.window performClose:sender]; } diff --git a/MacPass/MPInspectorViewController.m b/MacPass/MPInspectorViewController.m index 8696cd35..95880310 100644 --- a/MacPass/MPInspectorViewController.m +++ b/MacPass/MPInspectorViewController.m @@ -64,14 +64,6 @@ typedef NS_ENUM(NSUInteger, MPContentTab) { self.groupViewController = [[MPGroupInspectorViewController alloc] init]; self.didPushHistory = NO; /* subviewcontrollers will notify us about a change so we can handle the history pushing */ - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(_willChangeValueForRepresentedObjectNotification:) - name:MPViewControllerWillChangeValueForRepresentedObjectKeyPathNotification - object:self.entryViewController]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(_willChangeValueForRepresentedObjectNotification:) - name:MPViewControllerWillChangeValueForRepresentedObjectKeyPathNotification - object:self.groupViewController]; } return self; } @@ -121,6 +113,12 @@ typedef NS_ENUM(NSUInteger, MPContentTab) { selector:@selector(_didChangeCurrentItem:) name:MPDocumentCurrentItemChangedNotification object:document]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(_willChangeValueForRepresentedObjectNotification:) + name:MPDocumentWillChangeModelPropertyNotification + object:document]; + [self.entryViewController registerNotificationsForDocument:document]; } @@ -176,10 +174,6 @@ typedef NS_ENUM(NSUInteger, MPContentTab) { self.popover.delegate = self; self.popover.behavior = NSPopoverBehaviorTransient; MPIconSelectViewController *vc = [[MPIconSelectViewController alloc] init]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(_willChangeValueForRepresentedObjectNotification:) - name:MPViewControllerWillChangeValueForRepresentedObjectKeyPathNotification - object:vc]; vc.representedObject = self.representedObject; vc.popover = self.popover; self.popover.contentViewController = vc; @@ -195,10 +189,6 @@ typedef NS_ENUM(NSUInteger, MPContentTab) { self.popover.delegate = self; self.popover.behavior = NSPopoverBehaviorTransient; MPDatePickingViewController *vc = [[MPDatePickingViewController alloc] init]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(_willChangeValueForRepresentedObjectNotification:) - name:MPViewControllerWillChangeValueForRepresentedObjectKeyPathNotification - object:vc]; vc.representedObject = self.representedObject; self.popover.contentViewController = vc; [self.popover showRelativeToRect:NSZeroRect ofView:sender preferredEdge:NSMinYEdge]; @@ -210,26 +200,12 @@ typedef NS_ENUM(NSUInteger, MPContentTab) { - (void)popoverDidClose:(NSNotification *)notification { /* clear out the popover */ - NSPopover *po = notification.object; - [[NSNotificationCenter defaultCenter] removeObserver:self name:MPViewControllerWillChangeValueForRepresentedObjectKeyPathNotification object:po.contentViewController]; self.popover = nil; } #pragma mark - -#pragma mark Bindings - -- (void)willChangeValueForRepresentedObjectKeyPath:(NSString *)keyPath { - [super willChangeValueForKey:keyPath]; - [self _recordChangesForCurrentNode]; -} - -#pragma mark - -#pragma mark MPViewController Notifications -- (void)_willChangeValueForRepresentedObjectNotification:(NSNotification *)notification { - [self _recordChangesForCurrentNode]; -} - -- (void)_recordChangesForCurrentNode { +#pragma mark MPDocument Notifications +- (void)_willChangeModelProperty:(NSNotification *)notification { /* TODO use uuids for pushed item? */ if(self.didPushHistory) { return; @@ -241,9 +217,6 @@ typedef NS_ENUM(NSUInteger, MPContentTab) { } } -#pragma mark - -#pragma mark MPDocument Notifications - - (void)_didChangeCurrentItem:(NSNotification *)notification { MPDocument *document = notification.object; KPKNode *node = document.selectedNodes.count == 1 ? document.selectedNodes.firstObject : nil; diff --git a/MacPass/MPModelChangeObserving.h b/MacPass/MPModelChangeObserving.h deleted file mode 100644 index 16db5d2a..00000000 --- a/MacPass/MPModelChangeObserving.h +++ /dev/null @@ -1,41 +0,0 @@ -// -// MPModelChangeObserving.h -// MacPass -// -// Created by Michael Starke on 26/08/16. -// Copyright © 2016 HicknHack Software GmbH. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@protocol MPModelChangeObserving - -/* A class conforming to the protocoll will shoudl always fire the appropriate notifications listes below - - You need to overwrite setValue:forKeyPath in a conforming class. For convinience you can just call the helper and lett it do the job - - */ -FOUNDATION_EXTERN NSString *const MPWillChangeModelNotification; -FOUNDATION_EXTERN NSString *const MPDidChangeModelNotification; - -FOUNDATION_EXTERN NSString *const MPModelChangeObservingKeyPathKey; - -@required -- (void)beginObservingModelChangesForKeyPath:(NSString *)keyPath; -- (void)endObservingModelChangesForKeyPath:(NSString *)keyPath; -@end - -/* Use this helper to fire the right notifications. You can hold an instance to help in you implementation of setValue:forKeyPath and observerModelChangesForKeyPath: */ -@interface MPModelChangeObservingHelper : NSObject - -+ (void)willChangeModelKeyPath:(NSString *)keyPath observer:(id)observer; -+ (void)didChangeModelKeyPath:(NSString *)keyPath observer:(id)observer; -- (void)beginObservingModelChangesForKeyPath:(NSString *)keyPath; -- (void)endObservingModelChangesForKeyPath:(NSString *)keyPath; -- (void)setValue:(id)value forKeyPath:(NSString *)keyPath forTarget:(id)target; - -@end - -NS_ASSUME_NONNULL_END \ No newline at end of file diff --git a/MacPass/MPModelChangeObserving.m b/MacPass/MPModelChangeObserving.m deleted file mode 100644 index 60432a94..00000000 --- a/MacPass/MPModelChangeObserving.m +++ /dev/null @@ -1,75 +0,0 @@ -// -// MPModelChangeObserver.m -// MacPass -// -// Created by Michael Starke on 26/08/16. -// Copyright © 2016 HicknHack Software GmbH. All rights reserved. -// - -#import "MPModelChangeObserving.h" - -NSString *const MPWillChangeModelNotification = @"com.hicknhack.macpass.MPWillChangeModelNotification"; -NSString *const MPDidChangeModelNotification = @"com.hicknhack.macpass.MPDidChangeModelNotification"; - -NSString *const MPModelChangeObservingKeyPathKey = @"MPModelChangeObservingKeyPathKey"; - -@interface MPModelChangeObservingHelper () -@property (strong) NSMutableSet *observedPaths; -@property (strong) NSMutableSet *matchedPathsCache; -@end - -@implementation MPModelChangeObservingHelper - -+ (void)willChangeModelKeyPath:(NSString *)keyPath observer:(id)observer { - [[NSNotificationCenter defaultCenter] postNotificationName:MPWillChangeModelNotification object:observer userInfo:@{ MPModelChangeObservingKeyPathKey : keyPath }]; -} - -+ (void)didChangeModelKeyPath:(NSString *)keyPath observer:(id)observer { - [[NSNotificationCenter defaultCenter] postNotificationName:MPDidChangeModelNotification object:observer userInfo:@{ MPModelChangeObservingKeyPathKey : keyPath }]; -} - -- (void)setValue:(id)value forKeyPath:(NSString *)keyPath forTarget:(id)target { - [MPModelChangeObservingHelper willChangeModelKeyPath:keyPath observer:target]; - [target setValue:value forKeyPath:keyPath]; - [MPModelChangeObservingHelper didChangeModelKeyPath:keyPath observer:target]; -} - -- (void)beginObservingModelChangesForKeyPath:(NSString *)keyPath { - if(!self.observedPaths) { - self.observedPaths = [[NSMutableSet alloc] initWithCapacity:3]; - } - [self.observedPaths addObject:keyPath]; -} - -- (void)endObservingModelChangesForKeyPath:(NSString *)keyPath { - [self.observedPaths removeObject:keyPath]; - /* if we have nothing to observer, just clear the cache and exit */ - if(self.observedPaths.count == 0) { - self.matchedPathsCache = nil; - return; - } - - NSPredicate *predicat = [NSPredicate predicateWithBlock:^BOOL(id _Nonnull evaluatedObject, NSDictionary * _Nullable bindings) { - NSString *cachedPath = evaluatedObject; - return ![cachedPath hasPrefix:keyPath]; - }]; - [self.matchedPathsCache filterUsingPredicate:predicat]; -} - -- (BOOL)_isObservingKeyPath:(NSString *)keyPath { - if([self.matchedPathsCache containsObject:keyPath]) { - return YES; - } - for(NSString *observedKeyPath in self.observedPaths) { - if([keyPath hasPrefix:observedKeyPath]) { - if(!self.matchedPathsCache) { - self.matchedPathsCache = [[NSMutableSet alloc] initWithCapacity:3]; - } - [self.matchedPathsCache addObject:keyPath]; - return YES; - } - } - return NO; -} - -@end diff --git a/MacPass/MPViewController.h b/MacPass/MPViewController.h index aacae7ee..00f5462f 100644 --- a/MacPass/MPViewController.h +++ b/MacPass/MPViewController.h @@ -10,33 +10,10 @@ @interface MPViewController : NSViewController -APPKIT_EXTERN NSString *const MPViewControllerWillChangeValueForRepresentedObjectKeyPathNotification; -APPKIT_EXTERN NSString *const MPViewControllerDidChangeValueForRepresentedObjectKeyPathNotification; - @property (nonatomic, readonly) NSWindowController *windowController; - (void)didLoadView; - (NSResponder *)reconmendedFirstResponder; - (void)updateResponderChain; -#pragma mark Binding Observation -/** - * Override this to get notificied when setValue:forKeyPath will be called with a keypath starting with representedObject. - * This is always called via the binding system, hence it's usefull to anticipate model changes via the ui - * - * The default implementation calls this just befor setValue:forKeyPath: and posts a MPViewControllerWillChangeValueForRepresentedObjectKeyPathNotification - * If you override this, you shoudl call super to ensure well defined behaviour - * - * @param keyPath the full key path about to be affected - */ -- (void)willChangeValueForRepresentedObjectKeyPath:(NSString *)keyPath; -/** - * Override this to get notified when setValue:forKeyPath was called with a keypath starting with representedObject. - * The default implementation calls this right after setValue:forKeyPath: and will post a MPViewControllerDidChangeValueForRepresentedObjectKeyPathNotification. - * If you override this, you should call super to ensure well defined behavoir - * - * @param keyPath the full key path affected - */ -- (void)didChangeValueForRepresentedObjectKeyPath:(NSString *)keyPath; - @end diff --git a/MacPass/MPViewController.m b/MacPass/MPViewController.m index d9af4dc7..21967bb7 100644 --- a/MacPass/MPViewController.m +++ b/MacPass/MPViewController.m @@ -7,9 +7,7 @@ // #import "MPViewController.h" - -NSString *const MPViewControllerWillChangeValueForRepresentedObjectKeyPathNotification = @"com.hicknhack.macpass.MPViewControllerWillChangeValueForRepresentedObjectKeyPathNotification"; -NSString *const MPViewControllerDidChangeValueForRepresentedObjectKeyPathNotification = @"comt.hicknhack.macpass.MPViewControllerDidChangeValueForRepresentedObjectKeyPathNotification"; +#import "MPDocument.h" @implementation MPViewController @@ -43,21 +41,13 @@ NSString *const MPViewControllerDidChangeValueForRepresentedObjectKeyPathNotific #pragma mark Binding observation - (void)setValue:(id)value forKeyPath:(NSString *)keyPath { if([keyPath hasPrefix:@"representedObject."]) { - [self willChangeValueForRepresentedObjectKeyPath:keyPath]; + [((MPDocument *)self.windowController.document) willChangeModelProperty]; [super setValue:value forKeyPath:keyPath]; - [self didChangeValueForRepresentedObjectKeyPath:keyPath]; + [((MPDocument *)self.windowController.document) didChangeModelProperty]; } else { [super setValue:value forKeyPath:keyPath]; } } -- (void)willChangeValueForRepresentedObjectKeyPath:(NSString *)keyPath { - [[NSNotificationCenter defaultCenter] postNotificationName:MPViewControllerWillChangeValueForRepresentedObjectKeyPathNotification object:self]; -} - -- (void)didChangeValueForRepresentedObjectKeyPath:(NSString *)keyPath { - [[NSNotificationCenter defaultCenter] postNotificationName:MPViewControllerDidChangeValueForRepresentedObjectKeyPathNotification object:self]; -} - @end diff --git a/MacPassTests/MPTestModelChangeObservingHelper.m b/MacPassTests/MPTestModelChangeObservingHelper.m deleted file mode 100644 index b5161a83..00000000 --- a/MacPassTests/MPTestModelChangeObservingHelper.m +++ /dev/null @@ -1,43 +0,0 @@ -// -// MPTestModelChangeObservingHelper.m -// MacPass -// -// Created by Michael Starke on 29/08/16. -// Copyright © 2016 HicknHack Software GmbH. All rights reserved. -// - -#import -#import "MPModelChangeObserving.h" - -@interface MPTestModelChangeObservingHelper : XCTestCase -@property (strong) MPModelChangeObservingHelper *helper; -@end - -@implementation MPTestModelChangeObservingHelper - -- (void)setUp { - [super setUp]; - self.helper = [[MPModelChangeObservingHelper alloc] init]; -} - -- (void)tearDown { - self.helper = nil; - [super tearDown]; -} - -- (void)testAddObserver { - [self.helper beginObservingModelChangesForKeyPath:@"testKey"]; - NSMutableSet *set = [self.helper valueForKey:@"observedPaths"]; - XCTAssertEqual(set.count, 1, @"Observed paths contains one element"); - XCTAssertTrue([set containsObject:@"testKey"], @"Observed set contains testKey"); -} - -- (void)testRemoveObserver { - NSString *aKeyPath = @"testKeyPath"; - [self.helper beginObservingModelChangesForKeyPath:aKeyPath]; - [self.helper endObservingModelChangesForKeyPath:aKeyPath]; - NSMutableSet *set = [self.helper valueForKey:@"observedPaths"]; - XCTAssertFalse([set containsObject:aKeyPath], @"Observed path is removed after end of observation"); -} - -@end