Revision: 26188 http://sourceforge.net/p/bibdesk/svn/26188 Author: hofman Date: 2021-06-09 22:54:23 +0000 (Wed, 09 Jun 2021) Log Message: ----------- Follow field editor for complex string editor rather than the control, so we can have it owned by the formatter without creating a retain loop
Modified Paths: -------------- trunk/bibdesk/BDSKComplexStringCell.h trunk/bibdesk/BDSKComplexStringCell.m trunk/bibdesk/BDSKComplexStringEditor.h trunk/bibdesk/BDSKComplexStringEditor.m trunk/bibdesk/BDSKComplexStringFormatter.h trunk/bibdesk/BDSKComplexStringFormatter.m trunk/bibdesk/BDSKEditor.m trunk/bibdesk/Base.lproj/BDSKEditor.xib trunk/bibdesk/Base.lproj/MacroWindow.xib trunk/bibdesk/Base.lproj/TextImport.xib Modified: trunk/bibdesk/BDSKComplexStringCell.h =================================================================== --- trunk/bibdesk/BDSKComplexStringCell.h 2021-06-09 16:06:44 UTC (rev 26187) +++ trunk/bibdesk/BDSKComplexStringCell.h 2021-06-09 22:54:23 UTC (rev 26188) @@ -41,15 +41,3 @@ @interface BDSKComplexStringCell : NSTextFieldCell @end - -@class BDSKComplexStringEditor; - -@interface NSWindow (BDSKComplexStringwindow) -- (BDSKComplexStringEditor *)complexStringEditor:(BOOL)create; -@end - -@interface BDSKComplexStringWindow : NSWindow { - BDSKComplexStringEditor *complexStringEditor; -} -@end - Modified: trunk/bibdesk/BDSKComplexStringCell.m =================================================================== --- trunk/bibdesk/BDSKComplexStringCell.m 2021-06-09 16:06:44 UTC (rev 26187) +++ trunk/bibdesk/BDSKComplexStringCell.m 2021-06-09 22:54:23 UTC (rev 26188) @@ -38,49 +38,20 @@ #import "BDSKComplexStringCell.h" #import "BDSKComplexStringFormatter.h" -#import "BDSKComplexStringEditor.h" @implementation BDSKComplexStringCell -- (void)attachComplexStringEditorIfNeeded { - if ([[self formatter] respondsToSelector:@selector(editAsComplexString)] && - [(BDSKComplexStringFormatter *)[self formatter] editAsComplexString]) - [[[[self controlView] window] complexStringEditor:YES] attachToCell:self]; -} - - (void)editWithFrame:(NSRect)rect inView:(NSView *)controlView editor:(NSText *)textObj delegate:(id)delegate event:(NSEvent *)event { [super editWithFrame:rect inView:controlView editor:textObj delegate:delegate event:event]; - [self attachComplexStringEditorIfNeeded]; + if ([[self formatter] respondsToSelector:@selector(didStartEditing:enabled:)]) + [(BDSKComplexStringFormatter *)[self formatter] didStartEditing:textObj enabled:[self isEditable]]; } - (void)selectWithFrame:(NSRect)rect inView:(NSView *)controlView editor:(NSText *)textObj delegate:(id)delegate start:(NSInteger)selStart length:(NSInteger)selLength { [super selectWithFrame:rect inView:controlView editor:textObj delegate:delegate start:selStart length:selLength]; - [self attachComplexStringEditorIfNeeded]; + if ([[self formatter] respondsToSelector:@selector(didStartEditing:enabled:)]) + [(BDSKComplexStringFormatter *)[self formatter] didStartEditing:textObj enabled:[self isEditable]]; } @end - - -@implementation NSWindow (BDSKComplexStringWindow) - -- (BDSKComplexStringEditor *)complexStringEditor:(BOOL)create { return nil; } - -@end - - -@implementation BDSKComplexStringWindow - -- (void)dealloc { - BDSKDESTROY(complexStringEditor); - [super dealloc]; -} - -- (BDSKComplexStringEditor *)complexStringEditor:(BOOL)create { - if (complexStringEditor == nil && create) - complexStringEditor = [[BDSKComplexStringEditor alloc] init]; - return complexStringEditor; -} - -@end - Modified: trunk/bibdesk/BDSKComplexStringEditor.h =================================================================== --- trunk/bibdesk/BDSKComplexStringEditor.h 2021-06-09 16:06:44 UTC (rev 26187) +++ trunk/bibdesk/BDSKComplexStringEditor.h 2021-06-09 22:54:23 UTC (rev 26188) @@ -45,11 +45,13 @@ NSTextField *expandedValueTextField; BDSKBackgroundView *backgroundView; NSFormatter *formatter; - NSView *control; + NSText *editor; } -- (void)attachToCell:(NSCell *)cell; +- (id)initWithFormatter:(NSFormatter *)aFormatter; +- (void)attachToEditor:(NSText *)textObj enabled:(BOOL)isEnabled; + - (void)remove; @property (nonatomic, assign) IBOutlet NSTextField *expandedValueTextField; Modified: trunk/bibdesk/BDSKComplexStringEditor.m =================================================================== --- trunk/bibdesk/BDSKComplexStringEditor.m 2021-06-09 16:06:44 UTC (rev 26187) +++ trunk/bibdesk/BDSKComplexStringEditor.m 2021-06-09 22:54:23 UTC (rev 26188) @@ -44,12 +44,12 @@ @interface BDSKComplexStringEditor (Private) -- (void)displayValue:(NSString *)stringValue isError:(BOOL)isError; +- (void)editorFrameDidChange:(NSNotification *)notification; +- (void)editorWindowDidChangeKey:(NSNotification *)notification; +- (void)editorWindowWillClose:(NSNotification *)notification; +- (void)editorTextDidEndEditing:(NSNotification *)notification; +- (void)editorTextDidChange:(NSNotification *)notification; -- (void)controlFrameDidChange:(NSNotification *)notification; -- (void)controlWindowDidChangeKey:(NSNotification *)notification; -- (void)controlWindowWillClose:(NSNotification *)notification; - @end @implementation BDSKComplexStringEditor @@ -57,91 +57,97 @@ @synthesize expandedValueTextField, backgroundView; @dynamic attached; -- (id)init { +- (id)initWithFormatter:(NSFormatter *)aFormatter { self = [super initWithWindowNibName:@"ComplexStringEditor"]; if (self) { - control = nil; - formatter = nil; + editor = nil; + formatter = aFormatter; // not retained as it owns us } return self; } +- (id)init { + return [self initWithFormatter:nil]; +} + - (void)dealloc { - BDSKDESTROY(control); - BDSKDESTROY(formatter); + formatter = nil; + BDSKDESTROY(editor); [super dealloc]; } -- (void)attachToCell:(NSCell *)cell { +- (void)attachToEditor:(NSText *)textObj enabled:(BOOL)isEnabled { if ([self isAttached]) [self remove]; - control = [[cell controlView] retain]; - formatter = [[cell formatter] retain]; + editor = [textObj retain]; // make sure we loaded the nib [self window]; - BOOL isEnabled = [cell isEditable]; - - [self displayValue:[cell objectValue] isError:NO]; + // update the value + [self editorTextDidChange:nil]; // reset the frame and show the window - [self controlFrameDidChange:nil]; + [self editorFrameDidChange:nil]; // draw the focus ring we are covering when enabled [backgroundView setShowFocusRing:isEnabled]; - // track changes in the text, the frame and the window's key status of the control + // the editor may be wrapped in a clipview, and perhaps a scrollview + NSView *view = [editor superview]; + NSView *contentView = [[view enclosingScrollView] contentView]; + if ([view isKindOfClass:[NSClipView class]] == NO) { + view = editor; + } else if (contentView == view) { + view = [view enclosingScrollView]; + contentView = [[view enclosingScrollView] contentView]; + } + + // track changes in the text, the frame and the window's key status of the editor NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; - NSView *contentView = [[control enclosingScrollView] contentView]; - NSWindow *controlWindow = [control window]; + NSWindow *editorWindow = [editor window]; [nc addObserver:self - selector:@selector(controlTextDidChange:) - name:NSControlTextDidChangeNotification - object:control]; + selector:@selector(editorTextDidChange:) + name:NSTextDidChangeNotification + object:editor]; [nc addObserver:self - selector:@selector(controlTextDidEndEditing:) - name:NSControlTextDidEndEditingNotification - object:control]; + selector:@selector(editorTextDidEndEditing:) + name:NSTextDidEndEditingNotification + object:editor]; // observe future changes in the frame and the key status of the window // if the target tableView has a scrollview, we should observe its content view, or we won't notice scrolling if (contentView) [nc addObserver:self - selector:@selector(controlFrameDidChange:) + selector:@selector(editorFrameDidChange:) name:NSViewBoundsDidChangeNotification object:contentView]; [nc addObserver:self - selector:@selector(controlFrameDidChange:) + selector:@selector(editorFrameDidChange:) name:NSViewFrameDidChangeNotification - object:control]; - if ([control isKindOfClass:[NSTableView class]]) - [nc addObserver:self - selector:@selector(controlFrameDidChange:) - name:NSTableViewColumnDidResizeNotification - object:control]; + object:view]; if (isEnabled) { [nc addObserver:self - selector:@selector(controlWindowDidChangeKey:) + selector:@selector(editorWindowDidChangeKey:) name:NSWindowDidBecomeKeyNotification - object:controlWindow]; + object:editorWindow]; [nc addObserver:self - selector:@selector(controlWindowDidChangeKey:) + selector:@selector(editorWindowDidChangeKey:) name:NSWindowDidResignKeyNotification - object:controlWindow]; + object:editorWindow]; } - if ([controlWindow isSheet]) + if ([editorWindow isSheet]) [nc addObserver:self - selector:@selector(controlWindowWillClose:) + selector:@selector(editorWindowWillClose:) name:NSWindowDidEndSheetNotification - object:[controlWindow sheetParent]]; + object:[editorWindow sheetParent]]; else [nc addObserver:self - selector:@selector(controlWindowWillClose:) + selector:@selector(editorWindowWillClose:) name:NSWindowWillCloseNotification - object:controlWindow]; + object:editorWindow]; } - (BOOL)isAttached { - return (control != nil); + return (editor != nil); } - (void)remove { @@ -150,11 +156,10 @@ // we're going away now, so we can unregister for the notifications we registered for earlier NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; - [nc removeObserver:self name:NSControlTextDidChangeNotification object:nil]; - [nc removeObserver:self name:NSControlTextDidEndEditingNotification object:nil]; + [nc removeObserver:self name:NSTextDidChangeNotification object:nil]; + [nc removeObserver:self name:NSTextDidEndEditingNotification object:nil]; [nc removeObserver:self name:NSViewFrameDidChangeNotification object:nil]; [nc removeObserver:self name:NSViewBoundsDidChangeNotification object:nil]; - [nc removeObserver:self name:NSTableViewColumnDidResizeNotification object:nil]; [nc removeObserver:self name:NSWindowDidBecomeKeyNotification object:nil]; [nc removeObserver:self name:NSWindowDidResignKeyNotification object:nil]; [nc removeObserver:self name:NSWindowWillCloseNotification object:nil]; @@ -161,8 +166,7 @@ [nc removeObserver:self name:NSWindowDidEndSheetNotification object:nil]; // release the temporary objects - BDSKDESTROY(control); // we should set this to nil, as we use this as a flag that we are editing - BDSKDESTROY(formatter); + BDSKDESTROY(editor); // we should set this to nil, as we use this as a flag that we are editing // close window after removing control to avoid a loop, see windowWillClose: [[[self window] parentWindow] removeChildWindow:[self window]]; @@ -193,50 +197,56 @@ #pragma mark Frame change and keywindow notification handlers -- (void)controlFrameDidChange:(NSNotification *)notification { - NSRectEdge lowerEdge = [control isFlipped] ? NSMaxYEdge : NSMinYEdge; - NSRect lowerEdgeRect = [control bounds]; - NSRect winFrame = [[self window] frame]; - CGFloat margin = 4.0; // for the shadow and focus ring - CGFloat minWidth = 16.0; // minimal width of the window without margins, so subviews won't get shifted - - if ([control isKindOfClass:[NSTableView class]]) { - // sanity check, should never happen - if ([(NSTableView *)control editedRow] == -1) { +- (void)editorFrameDidChange:(NSNotification *)notification { + // the editor may be wrapped in a clipview and perhaps a scrollview + NSView *view = [editor superview]; + NSView *contentView = [[view enclosingScrollView] contentView]; + if ([view isKindOfClass:[NSClipView class]] == NO) { + // should not happen + if (view == nil) { [self remove]; return; } - lowerEdgeRect = [(NSTableView *)control frameOfCellAtColumn:[(NSTableView *)control editedColumn] row:[(NSTableView *)control editedRow]]; + view = editor; + } else if (contentView == view) { + view = [view enclosingScrollView]; + contentView = [[view enclosingScrollView] contentView]; } - lowerEdgeRect = BDSKSliceRect(lowerEdgeRect, 1.0, lowerEdge); - lowerEdgeRect = NSIntersectionRect(lowerEdgeRect, [control visibleRect]); + + NSRectEdge lowerEdge = [view isFlipped] ? NSMaxYEdge : NSMinYEdge; + NSRect lowerEdgeRect = NSInsetRect([view bounds], -2.0, -2.0); + NSRect winFrame = [[self window] frame]; + + lowerEdgeRect = BDSKSliceRect(lowerEdgeRect, 1.0, lowerEdge); + if (contentView) + lowerEdgeRect = NSIntersectionRect(lowerEdgeRect, [view convertRect:[contentView visibleRect] fromView:contentView]); // see if the cell's lower edge is scrolled out of sight if (NSIsEmptyRect(lowerEdgeRect)) { if ([self isWindowVisible]) { - [[control window] removeChildWindow:[self window]]; + [[[self window] parentWindow] removeChildWindow:[self window]]; [[self window] orderOut:self]; } return; } - lowerEdgeRect = [control convertRectToScreen:lowerEdgeRect]; // takes into account isFlipped + lowerEdgeRect = [view convertRectToScreen:lowerEdgeRect]; // takes into account isFlipped winFrame.origin = lowerEdgeRect.origin; winFrame.origin.y -= NSHeight(winFrame); - winFrame.size.width = fmax(NSWidth(lowerEdgeRect), minWidth); - winFrame = NSInsetRect(winFrame, -margin, 0.0); + winFrame.size.width = fmax(NSWidth(lowerEdgeRect), 16.0); // minimal width of the window without margins, so subviews won't get shifted + winFrame = NSInsetRect(winFrame, -4.0, 0.0); // for the shadow and focus ring [[self window] setFrame:winFrame display:YES]; if ([self isWindowVisible] == NO) { - [[control window] addChildWindow:[self window] ordered:NSWindowAbove]; + [[editor window] addChildWindow:[self window] ordered:NSWindowAbove]; [[self window] orderFront:self]; } } -- (void)controlWindowDidChangeKey:(NSNotification *)notification { +- (void)editorWindowDidChangeKey:(NSNotification *)notification { [backgroundView setShowFocusRing:[[notification object] isKeyWindow]]; } -- (void)controlWindowWillClose:(NSNotification *)notification { +- (void)editorWindowWillClose:(NSNotification *)notification { [self remove]; } @@ -250,20 +260,24 @@ } } -#pragma mark NSControl notification handlers +#pragma mark NSText notification handlers -- (void)controlTextDidEndEditing:(NSNotification *)notification { +- (void)editorTextDidEndEditing:(NSNotification *)notification { [self remove]; } -- (void)controlTextDidChange:(NSNotification *)notification { - NSString *string = [[[notification userInfo] objectForKey:@"NSFieldEditor"] string]; +- (void)editorTextDidChange:(NSNotification *)notification { + NSString *string = [editor string]; NSString *error = nil; - NSString *complexString = nil; - if ([formatter getObjectValue:&complexString forString:string errorDescription:&error]) - [self displayValue:complexString isError:NO]; - else - [self displayValue:error isError:YES]; + if ([formatter getObjectValue:&string forString:string errorDescription:&error]) { + [expandedValueTextField setObjectValue:string]; + [expandedValueTextField setTextColor:[NSColor systemRedColor]]; + [expandedValueTextField setToolTip:[NSString stringWithFormat:NSLocalizedString(@"Invalid BibTeX string: %@. This change will not be recorded.", @"Tool tip message"), string]]; + } else { + [expandedValueTextField setObjectValue:error]; + [expandedValueTextField setTextColor:[NSColor controlTextColor]]; + [expandedValueTextField setToolTip:NSLocalizedString(@"This field contains macros and is being edited as it would appear in a BibTeX file. This is the expanded value.", @"Tool tip message")]; + } } @end Modified: trunk/bibdesk/BDSKComplexStringFormatter.h =================================================================== --- trunk/bibdesk/BDSKComplexStringFormatter.h 2021-06-09 16:06:44 UTC (rev 26187) +++ trunk/bibdesk/BDSKComplexStringFormatter.h 2021-06-09 22:54:23 UTC (rev 26188) @@ -36,11 +36,12 @@ #import <Cocoa/Cocoa.h> -@class BDSKMacroResolver; +@class BDSKMacroResolver, BDSKComplexStringEditor; @interface BDSKComplexStringFormatter : NSFormatter { BDSKMacroResolver *macroResolver; BOOL editAsComplexString; + BDSKComplexStringEditor *complexStringEditor; } - (id)initWithMacroResolver:(BDSKMacroResolver *)aMacroResolver; @@ -47,4 +48,6 @@ @property (nonatomic) BOOL editAsComplexString; +- (void)didStartEditing:(NSText *)textObj enabled:(BOOL)isEnabled; + @end Modified: trunk/bibdesk/BDSKComplexStringFormatter.m =================================================================== --- trunk/bibdesk/BDSKComplexStringFormatter.m 2021-06-09 16:06:44 UTC (rev 26187) +++ trunk/bibdesk/BDSKComplexStringFormatter.m 2021-06-09 22:54:23 UTC (rev 26188) @@ -39,6 +39,7 @@ #import "NSString_BDSKExtensions.h" #import "NSError_BDSKExtensions.h" #import "BDSKMacroResolver.h" +#import "BDSKComplexStringEditor.h" #import "NSColor_BDSKExtensions.h" @implementation BDSKComplexStringFormatter @@ -54,6 +55,7 @@ if (self) { editAsComplexString = NO; macroResolver = [aMacroResolver retain]; + complexStringEditor = nil; } return self; } @@ -63,6 +65,10 @@ } - (void)dealloc { + // should not happen + if ([complexStringEditor isAttached]) + [complexStringEditor remove]; + BDSKDESTROY(complexStringEditor); BDSKDESTROY(macroResolver); [super dealloc]; } @@ -131,4 +137,22 @@ return YES; } +#pragma mark Complex string editor + +- (void)setEditAsComplexString:(BOOL)flag { + if (flag != editAsComplexString) { + editAsComplexString = flag; + if (editAsComplexString == NO && [complexStringEditor isAttached]) + [complexStringEditor remove]; + } +} + +- (void)didStartEditing:(NSText *)textObj enabled:(BOOL)isEnabled { + if (editAsComplexString) { + if (complexStringEditor == nil) + complexStringEditor = [[BDSKComplexStringEditor alloc] initWithFormatter:self]; + [complexStringEditor attachToEditor:textObj enabled:isEnabled]; + } +} + @end Modified: trunk/bibdesk/BDSKEditor.m =================================================================== --- trunk/bibdesk/BDSKEditor.m 2021-06-09 16:06:44 UTC (rev 26187) +++ trunk/bibdesk/BDSKEditor.m 2021-06-09 22:54:23 UTC (rev 26188) @@ -43,7 +43,6 @@ #import "NSImage_BDSKExtensions.h" #import "BDSKComplexString.h" #import "BDSKComplexStringFormatter.h" -#import "BDSKComplexStringCell.h" #import "BDSKComplexStringEditor.h" #import "BDSKScriptHookManager.h" #import "BDSKEdgeView.h" @@ -388,7 +387,6 @@ if (control == tableView) { // controlTextDidEndEditing: is not called when calling abortEditing [tableCellFormatter setEditAsComplexString:NO]; - [[[self window] complexStringEditor:NO] remove]; } } else { Modified: trunk/bibdesk/Base.lproj/BDSKEditor.xib =================================================================== --- trunk/bibdesk/Base.lproj/BDSKEditor.xib 2021-06-09 16:06:44 UTC (rev 26187) +++ trunk/bibdesk/Base.lproj/BDSKEditor.xib 2021-06-09 22:54:23 UTC (rev 26188) @@ -32,7 +32,7 @@ </customObject> <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/> <customObject id="-3" userLabel="Application" customClass="NSObject"/> - <window allowsToolTipsWhenApplicationIsInactive="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="74" userLabel="Window" customClass="BDSKComplexStringWindow"> + <window allowsToolTipsWhenApplicationIsInactive="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="74" userLabel="Window"> <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/> <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/> <rect key="contentRect" x="258" y="337" width="700" height="400"/> Modified: trunk/bibdesk/Base.lproj/MacroWindow.xib =================================================================== --- trunk/bibdesk/Base.lproj/MacroWindow.xib 2021-06-09 16:06:44 UTC (rev 26187) +++ trunk/bibdesk/Base.lproj/MacroWindow.xib 2021-06-09 22:54:23 UTC (rev 26188) @@ -18,7 +18,7 @@ </customObject> <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/> <customObject id="-3" userLabel="Application" customClass="NSObject"/> - <window title="Macros" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="Macro Editing Window" animationBehavior="default" id="5" userLabel="Window" customClass="BDSKComplexStringWindow"> + <window title="Macros" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="Macro Editing Window" animationBehavior="default" id="5" userLabel="Window"> <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/> <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/> <rect key="contentRect" x="179" y="349" width="376" height="275"/> Modified: trunk/bibdesk/Base.lproj/TextImport.xib =================================================================== --- trunk/bibdesk/Base.lproj/TextImport.xib 2021-06-09 16:06:44 UTC (rev 26187) +++ trunk/bibdesk/Base.lproj/TextImport.xib 2021-06-09 22:54:23 UTC (rev 26188) @@ -33,7 +33,7 @@ </customObject> <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/> <customObject id="-3" userLabel="Application" customClass="NSObject"/> - <window allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="TextImportWindow" animationBehavior="default" id="15" userLabel="Text2BibItemWindow" customClass="BDSKComplexStringWindow"> + <window allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="TextImportWindow" animationBehavior="default" id="15" userLabel="Text2BibItemWindow"> <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/> <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/> <rect key="contentRect" x="38" y="317" width="594" height="365"/> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. _______________________________________________ Bibdesk-commit mailing list Bibdesk-commit@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/bibdesk-commit